소스 검색

Digest Auth: the final part

Evgeny Grin (Karlson2k) 1 년 전
부모
커밋
aa428d5732
61개의 변경된 파일6685개의 추가작업 그리고 7164개의 파일을 삭제
  1. 4 0
      src/examples2/Makefile.am
  2. 296 0
      src/examples2/minimal_auth_digest.c
  3. 4 4
      src/incl_priv/mhd_sys_options.h
  4. 8 17
      src/include/d_options.rec
  5. 186 132
      src/include/microhttpd2.h
  6. 42 92
      src/include/microhttpd2_generated_daemon_options.h
  7. 151 108
      src/include/microhttpd2_main.h.in
  8. 25 1
      src/include/microhttpd2_preamble.h.in
  9. 38 1
      src/mhd2/Makefile.am
  10. 18 0
      src/mhd2/action.c
  11. 3386 0
      src/mhd2/auth_digest.c
  12. 85 0
      src/mhd2/auth_digest.h
  13. 6 13
      src/mhd2/daemon_options.h
  14. 6 9
      src/mhd2/daemon_set_options.c
  15. 133 15
      src/mhd2/daemon_start.c
  16. 8 0
      src/mhd2/lib_get_info.c
  17. 9 9
      src/mhd2/md5_ext.c
  18. 112 0
      src/mhd2/md5_ext.h
  19. 121 129
      src/mhd2/md5_int.c
  20. 129 0
      src/mhd2/md5_int.h
  21. 91 0
      src/mhd2/mhd_align.h
  22. 17 0
      src/mhd2/mhd_atomic_counter.c
  23. 20 0
      src/mhd2/mhd_atomic_counter.h
  24. 425 0
      src/mhd2/mhd_bithelpers.h
  25. 173 0
      src/mhd2/mhd_byteorder.h
  26. 114 2
      src/mhd2/mhd_daemon.h
  27. 20 5
      src/mhd2/mhd_digest_auth_data.h
  28. 110 0
      src/mhd2/mhd_md5.h
  29. 39 1
      src/mhd2/mhd_request.h
  30. 10 0
      src/mhd2/mhd_response.h
  31. 109 0
      src/mhd2/mhd_sha256.h
  32. 110 0
      src/mhd2/mhd_sha512_256.h
  33. 2 2
      src/mhd2/mhd_str.c
  34. 9 0
      src/mhd2/request_auth_get.c
  35. 3 0
      src/mhd2/request_auth_get.h
  36. 24 3
      src/mhd2/request_get_info.c
  37. 12 10
      src/mhd2/response_auth_digest.c
  38. 9 9
      src/mhd2/sha256_ext.c
  39. 19 19
      src/mhd2/sha256_ext.h
  40. 139 148
      src/mhd2/sha256_int.c
  41. 121 0
      src/mhd2/sha256_int.h
  42. 129 139
      src/mhd2/sha512_256_int.c
  43. 140 0
      src/mhd2/sha512_256_int.h
  44. 8 0
      src/mhd2/stream_funcs.c
  45. 8 1
      src/mhd2/stream_funcs.h
  46. 55 0
      src/mhd2/stream_process_reply.c
  47. 1 1
      src/mhd2/stream_process_request.c
  48. 1 1
      src/mhd2/stream_process_request.h
  49. 0 4082
      src/microhttpd/digestauth.c
  50. 0 84
      src/microhttpd/digestauth.h
  51. 0 682
      src/microhttpd/gen_auth.c
  52. 0 76
      src/microhttpd/gen_auth.h
  53. 0 131
      src/microhttpd/md5.h
  54. 0 113
      src/microhttpd/md5_ext.h
  55. 0 97
      src/microhttpd/mhd_align.h
  56. 0 402
      src/microhttpd/mhd_bithelpers.h
  57. 0 169
      src/microhttpd/mhd_byteorder.h
  58. 0 98
      src/microhttpd/mhd_md5_wrap.h
  59. 0 100
      src/microhttpd/mhd_sha256_wrap.h
  60. 0 122
      src/microhttpd/sha256.h
  61. 0 137
      src/microhttpd/sha512_256.h

+ 4 - 0
src/examples2/Makefile.am

@@ -32,3 +32,7 @@ noinst_PROGRAMS = \
 if MHD_SUPPORT_AUTH_BASIC
   noinst_PROGRAMS += minimal_auth_basic
 endif
+
+if MHD_SUPPORT_AUTH_DIGEST
+  noinst_PROGRAMS += minimal_auth_digest
+endif

+ 296 - 0
src/examples2/minimal_auth_digest.c

@@ -0,0 +1,296 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 minimal_auth_digest.c
+ * @brief  Minimal example for Digest Authentication
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <microhttpd2.h>
+#if defined(_WIN32) && ! defined(__CYGWIN__)
+#  include <wincrypt.h> /* For entropy generation */
+#else
+#  include <unistd.h> /* close() function */
+#endif /* _WIN32 && ! __CYGWIN__ */
+
+static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3)
+const struct MHD_Action *
+req_cb (void *cls,
+        struct MHD_Request *MHD_RESTRICT request,
+        const struct MHD_String *MHD_RESTRICT path,
+        enum MHD_HTTP_Method method,
+        uint_fast64_t upload_size)
+{
+  static const char secret_page[] = "Welcome to the cave of treasures!\n";
+  static const char auth_required_page[] =
+    "You need to know the secret to get in.\n";
+  static const char msg_forbidden_page[] =
+    "You are not allowed to enter. Go away!\n";
+  static const char msg_bad_header_page[] =
+    "The Authorization header data is invalid\n";
+  static const char realm[] = "The secret cave";
+  static const char allowed_username[] = "alibaba";
+  static const char allowed_password[] = "open sesam";
+  static const size_t allowed_username_len =
+    (sizeof(allowed_username) / sizeof(char) - 1);
+  union MHD_RequestInfoDynamicData req_data;
+  const struct MHD_AuthDigestInfo *uname; /* a shortcut */
+  enum MHD_StatusCode res;
+
+  (void) cls;
+  (void) path;
+  (void) method;
+  (void) upload_size; /* Unused */
+
+  res =
+    MHD_request_get_info_dynamic (request,
+                                  MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME,
+                                  &req_data);
+  if (MHD_SC_AUTH_ABSENT == res)
+    return MHD_action_digest_auth_challenge_a (
+      request,
+      realm,
+      "0",
+      NULL,
+      MHD_NO,
+      MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+      MHD_DIGEST_AUTH_MULT_ALGO_ANY,
+      MHD_NO,
+      MHD_YES,
+      MHD_response_from_buffer_static (
+        MHD_HTTP_STATUS_UNAUTHORIZED,
+        sizeof(auth_required_page) / sizeof(char) - 1,
+        auth_required_page));
+
+  if (MHD_SC_REQ_AUTH_DATA_BROKEN == res)
+    return MHD_action_from_response (
+      request,
+      MHD_response_from_buffer_static (
+        MHD_HTTP_STATUS_BAD_REQUEST,
+        sizeof(msg_bad_header_page) / sizeof(char) - 1,
+        msg_bad_header_page));
+
+  if (MHD_SC_OK != res)
+    return MHD_action_abort_request (request);
+
+  /* Assign result to short-named variable for convenience */
+  uname = req_data.v_auth_digest_info;
+  if ((uname->username.len == allowed_username_len) &&
+      (memcmp (allowed_username,
+               uname->username.cstr,
+               uname->username.len) == 0))
+  {
+    /* The client gave the correct username. Check the password match. */
+    enum MHD_DigestAuthResult auth_res;
+
+    auth_res = MHD_digest_auth_check (request,
+                                      realm,
+                                      allowed_username,
+                                      allowed_password,
+                                      0,
+                                      MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                      MHD_DIGEST_AUTH_MULT_ALGO_ANY);
+
+    if (MHD_DAUTH_OK == auth_res)
+      /* User authenticated */
+      return MHD_action_from_response (
+        request,
+        MHD_response_from_buffer_static (
+          MHD_HTTP_STATUS_OK,
+          sizeof(secret_page) / sizeof(char) - 1,
+          secret_page));
+
+    if (MHD_DAUTH_NONCE_STALE == auth_res)
+      return MHD_action_digest_auth_challenge_a (
+        request,
+        realm,
+        "0",
+        NULL,
+        MHD_YES /* Indicate "stale" nonce */,
+        MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+        MHD_DIGEST_AUTH_MULT_ALGO_ANY,
+        MHD_NO,
+        MHD_YES,
+        MHD_response_from_buffer_static (
+          MHD_HTTP_STATUS_UNAUTHORIZED,
+          sizeof(auth_required_page) / sizeof(char) - 1,
+          auth_required_page));
+
+    if (MHD_DAUTH_NONCE_WRONG <= auth_res)
+      /* Wrong password or attack attempt */
+      return MHD_action_from_response (
+        request,
+        MHD_response_from_buffer_static (
+          MHD_HTTP_STATUS_FORBIDDEN,
+          sizeof(msg_forbidden_page) / sizeof(char) - 1,
+          msg_forbidden_page));
+  }
+  /* Wrong username */
+
+  return MHD_action_from_response (
+    request,
+    MHD_response_from_buffer_static (
+      MHD_HTTP_STATUS_FORBIDDEN,
+      sizeof(msg_forbidden_page) / sizeof(char) - 1,
+      msg_forbidden_page));
+}
+
+
+static char entropy_bytes[32];
+
+static int
+init_entropy_bytes (void);
+
+int
+main (int argc,
+      char *const *argv)
+{
+  struct MHD_Daemon *d;
+  int port;
+
+  if (argc != 2)
+  {
+    fprintf (stderr,
+             "Usage:\n%s PORT\n",
+             argv[0]);
+    return 1;
+  }
+  port = atoi (argv[1]);
+  if ((1 > port) || (65535 < port))
+  {
+    fprintf (stderr,
+             "The PORT must be a numeric value between 1 and 65535.\n");
+    return 2;
+  }
+  if (! init_entropy_bytes ())
+    return 11;
+
+  d = MHD_daemon_create (&req_cb,
+                         NULL);
+  if (NULL == d)
+  {
+    fprintf (stderr,
+             "Failed to create MHD daemon.\n");
+    return 3;
+  }
+  if (MHD_SC_OK !=
+      MHD_DAEMON_SET_OPTIONS (
+        d,
+        MHD_D_OPTION_WM_WORKER_THREADS (1),
+        MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
+                                (uint_least16_t) port),
+        MHD_D_OPTION_RANDOM_ENTROPY (sizeof(entropy_bytes),
+                                     entropy_bytes)))
+  {
+    fprintf (stderr,
+             "Failed to set MHD daemon run parameters.\n");
+  }
+  else
+  {
+    if (MHD_SC_OK !=
+        MHD_daemon_start (d))
+    {
+      fprintf (stderr,
+               "Failed to start MHD daemon.\n");
+    }
+    else
+    {
+      printf ("The MHD daemon is listening on port %d\n"
+              "Press ENTER to stop.\n", port);
+      (void) fgetc (stdin);
+    }
+  }
+  printf ("Stopping... ");
+  fflush (stdout);
+  MHD_daemon_destroy (d);
+  printf ("OK\n");
+  return 0;
+}
+
+
+/**
+ * Initialise random data
+ * @return non-zero if succeed,
+ *         zero if failed
+ */
+static int
+init_entropy_bytes (void)
+{
+#if ! defined(_WIN32) || defined(__CYGWIN__)
+  int fd;
+  ssize_t len;
+  size_t off;
+
+  fd = open ("/dev/urandom", O_RDONLY);
+  if (-1 == fd)
+  {
+    fd = open ("/dev/arandom", O_RDONLY);
+    if (-1 == fd)
+      fd = open ("/dev/random", O_RDONLY);
+  }
+  if (0 > fd)
+  {
+    fprintf (stderr, "Failed to open random data source: %s\n",
+             strerror (errno));
+    return 0;
+  }
+  for (off = 0; off < sizeof (entropy_bytes); off += (size_t) len)
+  {
+    len = read (fd,
+                entropy_bytes + off,
+                sizeof (entropy_bytes) - off);
+    if (0 >= len)
+    {
+      fprintf (stderr, "Failed to read random data source: %s\n",
+               strerror (errno));
+      (void) close (fd);
+      return 0;
+    }
+  }
+  (void) close (fd);
+#else  /* Native W32 */
+  HCRYPTPROV cc;
+  BOOL b;
+
+  b = CryptAcquireContext (&cc,
+                           NULL,
+                           NULL,
+                           PROV_RSA_FULL,
+                           CRYPT_VERIFYCONTEXT);
+  if (FALSE == b)
+  {
+    fprintf (stderr,
+             "Failed to acquire crypto provider context: %lu\n",
+             (unsigned long) GetLastError ());
+    return 0;
+  }
+  b = CryptGenRandom (cc, sizeof(entropy_bytes), (BYTE *) entropy_bytes);
+  if (FALSE == b)
+  {
+    fprintf (stderr,
+             "Failed to generate random bytes: %lu\n",
+             GetLastError ());
+  }
+  CryptReleaseContext (cc, 0);
+  return (FALSE != b);
+#endif /* Native W32 */
+}

+ 4 - 4
src/incl_priv/mhd_sys_options.h

@@ -487,12 +487,12 @@
            (FUNC_ATTR_PTRCOMPARE_WORKS || FUNC_ATTR_NOSANITIZE_WORKS))   */
 
 #ifndef _MSC_FULL_VER
-#  define MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ /* empty */
-#  define MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ /* empty */
+#  define mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE /* empty */
+#  define mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE /* empty */
 #else  /* _MSC_FULL_VER */
-#  define MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ \
+#  define mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE \
         __pragma(runtime_checks("c", off))
-#  define MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ \
+#  define mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE \
         __pragma(runtime_checks("c", restore))
 #endif /* _MSC_FULL_VER */
 

+ 8 - 17
src/include/d_options.rec

@@ -423,6 +423,7 @@ Name: random_entropy
 Value: 400
 Comment: Set strong random data to be used by MHD.
 + Currently the data is only needed for Digest Auth module.
++ Daemon support for Digest Auth is enabled automatically if this option is used.
 + The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
 + Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
 Argument1: size_t buf_size
@@ -448,36 +449,26 @@ CustomSetter: /* custom setter */
 + }
 
 
-Name: dauth_map_size
+Name: auth_digest_map_size
 Value: 401
 Comment: Specify the size of the internal hash map array that tracks generated digest nonces usage.
 + When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
-+ By default the size is 8 bytes (very small).
++ By default the size is 1000 entries.
 Argument1: size_t size
 Description1: the size of the map array
 
-Name: dauth_nonce_bind_type
-Value: 402
-Type: enum MHD_DaemonOptionValueDAuthBindNonce
-Comment: Control the scope of validity of MHD-generated nonces.
-+ This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
-+ This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
-+ When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
-Argument1: enum MHD_DaemonOptionValueDAuthBindNonce bind_type
-Description1: FIXME
-
-Name: dauth_def_nonce_timeout
+Name: auth_digest_nonce_timeout
 Value: 403
-Comment: Default nonce timeout value (in seconds) used for Digest Auth.
-+ Silently ignored if followed by zero value.
+Comment: Nonce validity time (in seconds) used for Digest Auth.
++ If followed by zero value the value is silently ignored.
 + @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
 Argument1: unsigned int timeout
 Description1: FIXME
 
-Name: dauth_def_max_nc
+Name: auth_digest_def_max_nc
 Value: 404
 Comment: Default maximum nc (nonce count) value used for Digest Auth.
-+ Silently ignored if followed by zero value.
++ If followed by zero value the value is silently ignored.
 + @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
 Argument1: uint_fast32_t max_nc
 Description1: FIXME

+ 186 - 132
src/include/microhttpd2.h

@@ -1230,6 +1230,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_REPLY_FILE_READ_ERROR = 50232
   ,
+  /**
+   * Failed to generate the nonce for the Digest Auth.
+   */
+  MHD_SC_REPLY_NONCE_ERROR = 50233
+  ,
   /**
    * Failed to allocate memory in connection's pool for the reply.
    */
@@ -1379,6 +1384,14 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_TLS_CONNECTION_HANDSHAKED_FAILED = 51220
   ,
+  /**
+   * Hashing failed.
+   * Internal hashing function can never fail (and this code is never returned
+   * for them). External hashing function (like TLS backend-based) may fail
+   * for various reasons, like failure of hardware acccelerated hashing.
+   */
+  MHD_SC_HASH_FAILED = 51260
+  ,
   /**
    * Something wrong in the internal MHD logic.
    * This error should be never returned if MHD works as expected.
@@ -1592,6 +1605,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_UPGRADED_HANDLE_INVALID = 60161
   ,
+  /**
+   * The provided output buffer is too small.
+   */
+  MHD_SC_OUT_BUFF_TOO_SMALL = 60180
+  ,
   /**
    * The requested type of information is not recognised.
    */
@@ -1628,7 +1646,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
   MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED = 60240
   ,
   /**
-   * The the Digest Auth QOP value is unknown or not supported.
+   * The Digest Auth QOP value is unknown or not supported.
    */
   MHD_SC_AUTH_DIGEST_QOP_NOT_SUPPORTED = 60241
 };
@@ -3892,6 +3910,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
    */
   MHD_REQUEST_ENDED_FILE_ERROR = 51
   ,
+  /**
+   * The request was aborted due to error generating valid nonce for Digest Auth
+   * @ingroup request
+   */
+  MHD_REQUEST_ENDED_NONCE_ERROR = 52
+  ,
   /**
    * Closing the session since MHD is being shut down.
    * @ingroup request
@@ -4412,6 +4436,7 @@ MHD_D_OPTION_NOTIFY_STREAM (
 /**
  * Set strong random data to be used by MHD.
  * Currently the data is only needed for Digest Auth module.
+ * Daemon support for Digest Auth is enabled automatically if this option is used.
  * The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
  * Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
  * @param buf_size the size of the buffer
@@ -4427,49 +4452,36 @@ MHD_D_OPTION_RANDOM_ENTROPY (
 /**
  * Specify the size of the internal hash map array that tracks generated digest nonces usage.
  * When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
- * By default the size is 8 bytes (very small).
+ * By default the size is 1000 entries.
  * @param size the size of the map array
  * @return structure with the requested setting
  */
 struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_MAP_SIZE (
+MHD_D_OPTION_AUTH_DIGEST_MAP_SIZE (
   size_t size
   );
 
 /**
- * Control the scope of validity of MHD-generated nonces.
- * This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
- * This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
- * When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
- * @param bind_type FIXME
- * @return structure with the requested setting
- */
-struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_NONCE_BIND_TYPE (
-  enum MHD_DaemonOptionValueDAuthBindNonce bind_type
-  );
-
-/**
- * Default nonce timeout value (in seconds) used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * Nonce validity time (in seconds) used for Digest Auth.
+ * If followed by zero value the value is silently ignored.
  * @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
  * @param timeout FIXME
  * @return structure with the requested setting
  */
 struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_DEF_NONCE_TIMEOUT (
+MHD_D_OPTION_AUTH_DIGEST_NONCE_TIMEOUT (
   unsigned int timeout
   );
 
 /**
  * Default maximum nc (nonce count) value used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * If followed by zero value the value is silently ignored.
  * @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
  * @param max_nc FIXME
  * @return structure with the requested setting
  */
 struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_DEF_MAX_NC (
+MHD_D_OPTION_AUTH_DIGEST_DEF_MAX_NC (
   uint_fast32_t max_nc
   );
 
@@ -6945,7 +6957,7 @@ MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh,
                    void *MHD_RESTRICT recv_buf,
                    size_t *MHD_RESTRICT received_size,
                    uint_fast64_t max_wait_millisec)
-MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_(3,2)
 MHD_FN_PAR_OUT_ (4);
 
 
@@ -7000,7 +7012,7 @@ MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh,
                    size_t *MHD_RESTRICT sent_size,
                    uint_fast64_t max_wait_millisec,
                    enum MHD_Bool more_data_to_come)
-MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_(3,2)
 MHD_FN_PAR_OUT_ (4);
 
 
@@ -7098,7 +7110,7 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthAlgo
 {
   /**
    * Unknown or wrong algorithm type.
-   * Used in struct MHD_DigestAuthInfo to indicate client value that
+   * Used in struct MHD_AuthDigestInfo to indicate client value that
    * cannot by identified.
    */
   MHD_DIGEST_AUTH_ALGO_INVALID = 0
@@ -7145,12 +7157,12 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthAlgo
 
 
 /**
- * Get digest size for specified algorithm.
+ * Get digest size in bytes for specified algorithm.
  *
  * The size of the digest specifies the size of the userhash, userdigest
  * and other parameters which size depends on used hash algorithm.
  * @param algo the algorithm to check
- * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
+ * @return the size (in bytes) of the digest (either #MHD_MD5_DIGEST_SIZE or
  *         #MHD_SHA256_DIGEST_SIZE/MHD_SHA512_256_DIGEST_SIZE)
  *         or zero if the input value is not supported or not valid
  * @sa #MHD_digest_auth_calc_userdigest()
@@ -7314,15 +7326,18 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthMultiAlgo
  *                          upon return
  * @param bin_buf_size the size of the @a userhash_bin buffer, must be
  *                     at least #MHD_digest_get_hash_size() bytes long
- * @return MHD_SC_OK on success,
- *         error code otherwise
+ * @return #MHD_SC_OK on success,
+ *         #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ *         #MHD_SC_HASH_FAILED if hashing failed,
+ *         #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ *                                                unknown or unsupported.
  * @sa #MHD_digest_auth_calc_userhash_hex()
  * @ingroup authentication
  */
 MHD_EXTERN_ enum MHD_StatusCode
 MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo algo,
-                               const char *username,
-                               const char *realm,
+                               const char *MHD_RESTRICT username,
+                               const char *MHD_RESTRICT realm,
                                size_t bin_buf_size,
                                void *MHD_RESTRICT userhash_bin)
 MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_ (2)
@@ -7360,17 +7375,20 @@ MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4);
  * @param[out] userhash_hex the output buffer for userhash as hex string;
  *                          if this function succeeds, then this buffer has
  *                          #MHD_digest_get_hash_size()*2 chars long
- *                          userhash zero-terminated string
- * @return MHD_SC_OK on success,
- *         error code otherwise
+ *                          userhash string plus one zero-termination char
+ * @return #MHD_SC_OK on success,
+ *         #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ *         #MHD_SC_HASH_FAILED if hashing failed,
+ *         #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ *                                                unknown or unsupported.
  * @sa #MHD_digest_auth_calc_userhash()
  * @ingroup authentication
  */
 MHD_EXTERN_ enum MHD_StatusCode
 MHD_digest_auth_calc_userhash_hex (
   enum MHD_DigestAuthAlgo algo,
-  const char *username,
-  const char *realm,
+  const char *MHD_RESTRICT username,
+  const char *MHD_RESTRICT realm,
   size_t hex_buf_size,
   char userhash_hex[MHD_FN_PAR_DYN_ARR_SIZE_ (hex_buf_size)])
 MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_ (2)
@@ -7383,7 +7401,7 @@ MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4);
  * Values are sorted so simplified checks could be used.
  * For example:
  * * (value <= MHD_DIGEST_AUTH_UNAME_TYPE_INVALID) is true if no valid username
- *   is provided by the client
+ *   is provided by the client (not used currently)
  * * (value >= MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH) is true if username is
  *   provided in any form
  * * (value >= MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD) is true if username is
@@ -7393,7 +7411,8 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthUsernameType
 {
   /**
    * No username parameter in in Digest Authorization header.
-   * This should be treated as an error.
+   * Not used currently. Value #MHD_SC_REQ_AUTH_DATA_BROKEN is returned
+   * by #MHD_request_get_info_dynamic_sz() if the request has no username.
    */
   MHD_DIGEST_AUTH_UNAME_TYPE_MISSING = 0
   ,
@@ -7420,10 +7439,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthUsernameType
   /**
    * The invalid combination of username parameters are used by client.
    * Either:
-   * * both 'username' and 'username*' are used
-   * * 'username*' is used with 'userhash=true'
-   * * 'username*' used with invalid extended notation
-   * * 'username' is not hexadecimal string, while 'userhash' set to 'true'
+   * + both 'username' and 'username*' are used
+   * + 'username*' is used with 'userhash=true'
+   * + 'username*' used with invalid extended notation
+   * + 'username' is not hexadecimal string, while 'userhash' set to 'true'
+   * Not used currently. Value #MHD_SC_REQ_AUTH_DATA_BROKEN is returned
+   * by #MHD_request_get_info_dynamic_sz() if the request has broken username.
    */
   MHD_DIGEST_AUTH_UNAME_TYPE_INVALID = (1u << 0)
 };
@@ -7435,7 +7456,7 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthQOP
 {
   /**
    * Invalid/unknown QOP.
-   * Used in struct MHD_DigestAuthInfo to indicate client value that
+   * Used in struct MHD_AuthDigestInfo to indicate client value that
    * cannot by identified.
    */
   MHD_DIGEST_AUTH_QOP_INVALID = 0
@@ -7515,16 +7536,52 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthMultiQOP
 };
 
 /**
- * The invalid value of 'nc' parameter in client Digest Authorization header.
+ * The type of 'nc' (nonce count) value provided in the request
  */
-#define MHD_DIGEST_AUTH_INVALID_NC_VALUE        (0)
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthNC
+{
+  /**
+   * Readable hexdecimal non-zero number.
+   * The decoded value is placed in @a nc member of struct MHD_AuthDigestInfo
+   */
+  MHD_DIGEST_AUTH_NC_NUMBER = 1
+  ,
+  /**
+   * Readable zero number.
+   * Compliant clients should not use such values.
+   * Can be treated as invalid request.
+   */
+  MHD_DIGEST_AUTH_NC_ZERO = 2
+  ,
+  /**
+   * 'nc' value is not provided by the client.
+   * Unless old RFC 2069 mode is allowed, this should be treated as invalid
+   * request.
+   */
+  MHD_DIGEST_AUTH_NC_NONE = 3
+  ,
+  /**
+   * 'nc' value is too long to be decoded.
+   * Compliant clients should not use such values.
+   * Can be treated as invalid request.
+   */
+  MHD_DIGEST_AUTH_NC_TOO_LONG = 4
+  ,
+  /**
+   * 'nc' value is too large for uint32_t.
+   * Compliant clients should not use such values.
+   * Can be treated as request with a stale nonce or as invalid request.
+   */
+  MHD_DIGEST_AUTH_NC_TOO_LARGE = 5
+};
+
 
 /**
  * Information from Digest Authorization client's header.
  *
- * @see #MHD_REQUEST_INFO_DYNAMIC_DAUTH_REQ_INFO
+ * @see #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO
  */
-struct MHD_DigestAuthInfo
+struct MHD_AuthDigestInfo
 {
   /**
    * The algorithm as defined by client.
@@ -7569,7 +7626,7 @@ struct MHD_DigestAuthInfo
    *          data.
    * @sa #MHD_digest_auth_calc_userhash()
    */
-  uint8_t *userhash_bin;
+  const uint8_t *userhash_bin;
 
   /**
    * The size of the data pointed by @a userhash_bin.
@@ -7605,13 +7662,17 @@ struct MHD_DigestAuthInfo
   size_t cnonce_len;
 
   /**
-   * The nc parameter value.
+   * The type of 'nc' (nonce count) value provided in the request.
+   */
+  enum MHD_DigestAuthNC nc_type;
+
+  /**
+   * The nc (nonce count) parameter value.
    * Can be used by application to limit the number of nonce re-uses. If @a nc
    * is higher than application wants to allow, then "auth required" response
    * with 'stale=true' could be used to force client to retry with the fresh
    * 'nonce'.
-   * If not specified by client or does not have hexadecimal digits only, the
-   * value is #MHD_DIGEST_AUTH_INVALID_NC_VALUE.
+   * Set to zero when @a nc_type is not set to #MHD_DIGEST_AUTH_NC_NUMBER.
    */
   uint_fast32_t nc;
 };
@@ -7620,9 +7681,9 @@ struct MHD_DigestAuthInfo
 /**
  * Information from Digest Authorization client's header.
  *
- * @see #MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO
+ * @see #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME
  */
-struct MHD_DigestAuthUsernameInfo
+struct MHD_AuthDigestUsernameInfo
 {
   /**
    * The algorithm as defined by client.
@@ -7633,7 +7694,10 @@ struct MHD_DigestAuthUsernameInfo
   /**
    * The type of username used by client.
    * The 'invalid' and 'missing' types are not used in this structure,
-   * instead NULL is returned for #MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO.
+   * instead #MHD_SC_REQ_AUTH_DATA_BROKEN is returned when
+   * #MHD_request_get_info_dynamic_sz() called with
+   * #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME and the request has
+   * a broken username data.
    */
   enum MHD_DigestAuthUsernameType uname_type;
 
@@ -7648,7 +7712,7 @@ struct MHD_DigestAuthUsernameInfo
    * The buffer pointed by the @a username becomes invalid when a response
    * for the requested is provided (or request is aborted).
    */
-  struct MHD_String username;
+  struct MHD_StringNullable username;
 
   /**
    * The userhash string.
@@ -7659,7 +7723,7 @@ struct MHD_DigestAuthUsernameInfo
    * for the requested is provided (or request is aborted).
    * @sa #MHD_digest_auth_calc_userhash_hex()
    */
-  struct MHD_String userhash_hex;
+  struct MHD_StringNullable userhash_hex;
 
   /**
    * The userhash decoded to binary form.
@@ -7675,7 +7739,7 @@ struct MHD_DigestAuthUsernameInfo
    *          data.
    * @sa #MHD_digest_auth_calc_userhash()
    */
-  uint8_t *userhash_bin;
+  const uint8_t *userhash_bin;
 };
 
 
@@ -7693,45 +7757,66 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
   ,
   /**
    * General error, like "out of memory".
+   * Authentication may be valid, but cannot be checked.
    */
   MHD_DAUTH_ERROR = 0
   ,
   /**
-   * No "Authorization" header or wrong format of the header.
-   * Also may be returned if required parameters in client Authorisation header
-   * are missing or broken (in invalid format).
+   * No "Authorization" header for Digest Authentication.
+   */
+  MHD_DAUTH_HEADER_MISSING = -1
+  ,
+  /**
+   * Wrong format of the header.
+   * Also returned if required parameters in Authorization header are missing
+   * or broken (in invalid format).
+   */
+  MHD_DAUTH_HEADER_BROKEN = -9
+  ,
+  /**
+   * Unsupported algorithm.
    */
-  MHD_DAUTH_WRONG_HEADER = -1
+  MHD_DAUTH_UNSUPPORTED_ALGO = -10
+  ,
+  /**
+   * Unsupported 'qop'.
+   */
+  MHD_DAUTH_UNSUPPORTED_QOP = -11
+  ,
+  /**
+   * Incorrect userdigest size.
+   */
+  MHD_DAUTH_INVALID_USERDIGEST_SIZE = -15
   ,
   /**
    * Wrong 'username'.
    */
-  MHD_DAUTH_WRONG_USERNAME = -2
+  MHD_DAUTH_WRONG_USERNAME = -17
   ,
   /**
    * Wrong 'realm'.
    */
-  MHD_DAUTH_WRONG_REALM = -3
+  MHD_DAUTH_WRONG_REALM = -18
   ,
   /**
    * Wrong 'URI' (or URI parameters).
    */
-  MHD_DAUTH_WRONG_URI = -4
+  MHD_DAUTH_WRONG_URI = -19
   ,
   /**
    * Wrong 'qop'.
    */
-  MHD_DAUTH_WRONG_QOP = -5
+  MHD_DAUTH_WRONG_QOP = -20
   ,
   /**
    * Wrong 'algorithm'.
    */
-  MHD_DAUTH_WRONG_ALGO = -6
+  MHD_DAUTH_WRONG_ALGO = -21
   ,
   /**
    * Too large (>64 KiB) Authorization parameter value.
    */
-  MHD_DAUTH_TOO_LARGE = -15
+  MHD_DAUTH_TOO_LARGE = -22
   ,
   /* The different form of naming is intentionally used for the results below,
    * as they are more important */
@@ -7741,22 +7826,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
    * username and password to get the fresh 'nonce'.
    * The validity of the 'nonce' may be not checked.
    */
-  MHD_DAUTH_NONCE_STALE = -17
-  ,
-  /**
-   * The 'nonce' was generated by MHD for other conditions.
-   * This value is only returned if #MHD_D_O_DAUTH_NONCE_BIND_TYPE is set
-   * to anything other than #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
-   * The interpretation of this code could be different. For example, if
-   * #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_URI is set and client just used
-   * the same 'nonce' for another URI, the code could be handled as
-   * #MHD_DAUTH_NONCE_STALE as RFCs allow nonces re-using for other URIs
-   * in the same "protection space".
-   * However, if only #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_CLIENT_IP bit is set
-   * and it is know that clients have fixed IP addresses, this return code could
-   * be handled like #MHD_DAUTH_NONCE_WRONG.
-   */
-  MHD_DAUTH_NONCE_OTHER_COND = -18
+  MHD_DAUTH_NONCE_STALE = -25
   ,
   /**
    * The 'nonce' is wrong. May indicate an attack attempt.
@@ -7764,7 +7834,8 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
   MHD_DAUTH_NONCE_WRONG = -33
   ,
   /**
-   * The 'response' is wrong. May indicate an attack attempt.
+   * The 'response' is wrong. May indicate a wrong password used or
+   * an attack attempt.
    */
   MHD_DAUTH_RESPONSE_WRONG = -34
 };
@@ -7786,9 +7857,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
  * @param username the username to be authenticated, must be in clear text
  *                 even if userhash is used by the client
  * @param password the password matching the @a username (and the @a realm)
- * @param nonce_timeout the period of seconds since nonce generation, when
- *                      the nonce is recognised as valid and not stale;
- *                      if zero is specified then daemon default value is used.
  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
  *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
  *               returned;
@@ -7801,18 +7869,15 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
  * @ingroup authentication
  */
 MHD_EXTERN_ enum MHD_DigestAuthResult
-MHD_digest_auth_check (struct MHD_Request *request,
-                       const char *realm,
-                       const char *username,
-                       const char *password,
-                       unsigned int nonce_timeout,
+MHD_digest_auth_check (struct MHD_Request *MHD_RESTRICT request,
+                       const char *MHD_RESTRICT realm,
+                       const char *MHD_RESTRICT username,
+                       const char *MHD_RESTRICT password,
                        uint_fast32_t max_nc,
                        enum MHD_DigestAuthMultiQOP mqop,
                        enum MHD_DigestAuthMultiAlgo malgo)
-MHD_FN_PAR_NONNULL_ (1)
-MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
-MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3)
-MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
+MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4);
 
 
 /**
@@ -7841,15 +7906,18 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
  *                            #MHD_digest_get_hash_size() bytes of
  *                            userdigest upon return
  * @return #MHD_SC_OK on success,
- *         error code otherwise.
+ *         #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ *         #MHD_SC_HASH_FAILED if hashing failed,
+ *         #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ *                                                unknown or unsupported.
  * @sa #MHD_digest_auth_check_digest()
  * @ingroup authentication
  */
 MHD_EXTERN_ enum MHD_StatusCode
 MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo algo,
-                                 const char *username,
-                                 const char *realm,
-                                 const char *password,
+                                 const char *MHD_RESTRICT username,
+                                 const char *MHD_RESTRICT realm,
+                                 const char *MHD_RESTRICT password,
                                  size_t bin_buf_size,
                                  void *MHD_RESTRICT userdigest_bin)
 MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
@@ -7875,46 +7943,41 @@ MHD_FN_PAR_OUT_SIZE_ (6,5);
  * @param realm the realm for authorization of the client
  * @param username the username 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",
- *                   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_SHA512_256_DIGEST_SIZE,
  *                        #MHD_digest_get_hash_size())
- *   FIXME: why pass it if it must anyway match????
- * @param nonce_timeout the period of seconds since nonce generation, when
- *                      the nonce is recognised as valid and not stale;
- *                      if zero is specified then daemon default value is used.
+ * @param userdigest the precalculated binary hash of the string
+ *                   "username:realm:password",
+ *                   see #MHD_digest_auth_calc_userdigest()
  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
  *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
  *               returned;
  *               if zero is specified then daemon default value is used.
  * @param mqop the QOP to use
  * @param malgo digest algorithms allowed to use, fail if algorithm used
- *               by the client is not allowed by this parameter;
- *               more than one base algorithms (MD5, SHA-256, SHA-512/256)
- *               cannot be used at the same time for this function
- *               as @a userdigest must match specified algorithm
+ *              by the client is not allowed by this parameter;
+ *              more than one base algorithms (MD5, SHA-256, SHA-512/256)
+ *              cannot be used at the same time for this function
+ *              as @a userdigest must match specified algorithm
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
  * @sa #MHD_digest_auth_calc_userdigest()
  * @ingroup authentication
  */
 MHD_EXTERN_ enum MHD_DigestAuthResult
-MHD_digest_auth_check_digest (struct MHD_Request *request,
-                              const char *realm,
-                              const char *username,
-                              const void *userdigest,
+MHD_digest_auth_check_digest (struct MHD_Request *MHD_RESTRICT request,
+                              const char *MHD_RESTRICT realm,
+                              const char *MHD_RESTRICT username,
                               size_t userdigest_size,
-                              unsigned int nonce_timeout,
+                              const void *MHD_RESTRICT userdigest,
                               uint_fast32_t max_nc,
                               enum MHD_DigestAuthMultiQOP mqop,
                               enum MHD_DigestAuthMultiAlgo malgo)
 MHD_FN_PAR_NONNULL_ALL_
 MHD_FN_PAR_CSTR_ (2)
 MHD_FN_PAR_CSTR_ (3)
-MHD_FN_PAR_CSTR_ (4);
+MHD_FN_PAR_IN_SIZE_ (5, 4);
 
 
 /**
@@ -8241,15 +8304,6 @@ MHD_STATIC_INLINE_END_
 #endif /* ! MHD_NO_STATIC_INLINE */
 
 
-/**
- * Constant to indicate that the nonce of the provided
- * authentication code was wrong.
- * Used as return code by #MHD_digest_auth_check(),
- * #MHD_digest_auth_check_digest()
- * @ingroup authentication
- */
-#define MHD_INVALID_NONCE -1
-
 /**
  * Add Basic Authentication "challenge" to the response.
  *
@@ -8293,7 +8347,7 @@ MHD_response_add_auth_basic_challenge (
   struct MHD_Response *MHD_RESTRICT response,
   const char *MHD_RESTRICT realm,
   enum MHD_Bool prefer_utf8)
-MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2);
+MHD_FN_PAR_NONNULL_(2) MHD_FN_PAR_CSTR_ (2);
 
 #ifndef MHD_NO_STATIC_INLINE
 
@@ -9929,9 +9983,9 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_RequestInfoDynamicType
    * username is provided or the format of the username parameter is broken.
    * Pointers in the returned structure (if any) are valid until response
    * is provided for the request.
-   * The result is placed in @a v_dauth_username member.
+   * The result is placed in @a v_auth_digest_uname member.
    */
-  MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO = 41
+  MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME = 41
   ,
   /**
    * Returns pointer to information about digest auth in client request.
@@ -9939,9 +9993,9 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_RequestInfoDynamicType
    * the client or the format of the digest auth header is broken.
    * Pointers in the returned structure (if any) are valid until response
    * is provided for the request.
-   * The result is placed in @a v_dauth_info member.
+   * The result is placed in @a v_auth_digest_info member.
    */
-  MHD_REQUEST_INFO_DYNAMIC_DAUTH_REQ_INFO = 42
+  MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO = 42
   ,
   /**
    * Returns information about Basic Authentication credentials in the request.
@@ -9995,12 +10049,12 @@ union MHD_RequestInfoDynamicData
   /**
    * The information about client provided username for digest auth
    */
-  const struct MHD_DigestAuthUsernameInfo *v_dauth_username;
+  const struct MHD_AuthDigestUsernameInfo *v_auth_digest_uname;
 
   /**
    * The information about client's digest auth
    */
-  const struct MHD_DigestAuthInfo *v_dauth_info;
+  const struct MHD_AuthDigestInfo *v_auth_digest_info;
 
   /**
    * The username and password provided by the client's basic auth header.

+ 42 - 92
src/include/microhttpd2_generated_daemon_options.h

@@ -298,6 +298,7 @@ The specified callback will be called one time, after network initialisation, TL
   /**
    * Set strong random data to be used by MHD.
    * Currently the data is only needed for Digest Auth module.
+   * Daemon support for Digest Auth is enabled automatically if this option is used.
    * The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
    * Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
    */
@@ -307,34 +308,25 @@ The specified callback will be called one time, after network initialisation, TL
   /**
    * Specify the size of the internal hash map array that tracks generated digest nonces usage.
    * When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
-   * By default the size is 8 bytes (very small).
+   * By default the size is 1000 entries.
    */
-  MHD_D_O_DAUTH_MAP_SIZE = 401
+  MHD_D_O_AUTH_DIGEST_MAP_SIZE = 401
   ,
 
   /**
-   * Control the scope of validity of MHD-generated nonces.
-   * This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
-   * This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
-   * When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
-   */
-  MHD_D_O_DAUTH_NONCE_BIND_TYPE = 402
-  ,
-
-  /**
-   * Default nonce timeout value (in seconds) used for Digest Auth.
-   * Silently ignored if followed by zero value.
+   * Nonce validity time (in seconds) used for Digest Auth.
+   * If followed by zero value the value is silently ignored.
    * @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
    */
-  MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT = 403
+  MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT = 403
   ,
 
   /**
    * Default maximum nc (nonce count) value used for Digest Auth.
-   * Silently ignored if followed by zero value.
+   * If followed by zero value the value is silently ignored.
    * @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
    */
-  MHD_D_O_DAUTH_DEF_MAX_NC = 404
+  MHD_D_O_AUTH_DIGEST_DEF_MAX_NC = 404
   ,
 
   /**
@@ -799,28 +791,22 @@ union MHD_DaemonOptionValue
   struct MHD_DaemonOptionEntropySeed random_entropy;
 
   /**
-   * Value for #MHD_D_O_DAUTH_MAP_SIZE.
+   * Value for #MHD_D_O_AUTH_DIGEST_MAP_SIZE.
    * the size of the map array
    */
-  size_t dauth_map_size;
-
-  /**
-   * Value for #MHD_D_O_DAUTH_NONCE_BIND_TYPE.
-   * FIXME
-   */
-  enum MHD_DaemonOptionValueDAuthBindNonce dauth_nonce_bind_type;
+  size_t auth_digest_map_size;
 
   /**
-   * Value for #MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT.
+   * Value for #MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT.
    * FIXME
    */
-  unsigned int dauth_def_nonce_timeout;
+  unsigned int auth_digest_nonce_timeout;
 
   /**
-   * Value for #MHD_D_O_DAUTH_DEF_MAX_NC.
+   * Value for #MHD_D_O_AUTH_DIGEST_DEF_MAX_NC.
    * FIXME
    */
-  uint_fast32_t dauth_def_max_nc;
+  uint_fast32_t auth_digest_def_max_nc;
 
 };
 
@@ -1396,6 +1382,7 @@ The specified callback will be called one time, after network initialisation, TL
 /**
  * Set strong random data to be used by MHD.
  * Currently the data is only needed for Digest Auth module.
+ * Daemon support for Digest Auth is enabled automatically if this option is used.
  * The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
  * Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
  * @param buf_size the size of the buffer
@@ -1414,62 +1401,46 @@ The specified callback will be called one time, after network initialisation, TL
 /**
  * Specify the size of the internal hash map array that tracks generated digest nonces usage.
  * When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
- * By default the size is 8 bytes (very small).
+ * By default the size is 1000 entries.
  * @param size the size of the map array
  * @return structure with the requested setting
  */
-#  define MHD_D_OPTION_DAUTH_MAP_SIZE(size) \
+#  define MHD_D_OPTION_AUTH_DIGEST_MAP_SIZE(size) \
         MHD_NOWARN_COMPOUND_LITERALS_ \
           (const struct MHD_DaemonOptionAndValue) \
         { \
-          .opt = MHD_D_O_DAUTH_MAP_SIZE,  \
-          .val.dauth_map_size = (size) \
+          .opt = MHD_D_O_AUTH_DIGEST_MAP_SIZE,  \
+          .val.auth_digest_map_size = (size) \
         } \
         MHD_RESTORE_WARN_COMPOUND_LITERALS_
 /**
- * Control the scope of validity of MHD-generated nonces.
- * This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
- * This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
- * When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
- * @param bind_type FIXME
- * @return structure with the requested setting
- */
-#  define MHD_D_OPTION_DAUTH_NONCE_BIND_TYPE(bind_type) \
-        MHD_NOWARN_COMPOUND_LITERALS_ \
-          (const struct MHD_DaemonOptionAndValue) \
-        { \
-          .opt = MHD_D_O_DAUTH_NONCE_BIND_TYPE,  \
-          .val.dauth_nonce_bind_type = (bind_type) \
-        } \
-        MHD_RESTORE_WARN_COMPOUND_LITERALS_
-/**
- * Default nonce timeout value (in seconds) used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * Nonce validity time (in seconds) used for Digest Auth.
+ * If followed by zero value the value is silently ignored.
  * @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
  * @param timeout FIXME
  * @return structure with the requested setting
  */
-#  define MHD_D_OPTION_DAUTH_DEF_NONCE_TIMEOUT(timeout) \
+#  define MHD_D_OPTION_AUTH_DIGEST_NONCE_TIMEOUT(timeout) \
         MHD_NOWARN_COMPOUND_LITERALS_ \
           (const struct MHD_DaemonOptionAndValue) \
         { \
-          .opt = MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT,  \
-          .val.dauth_def_nonce_timeout = (timeout) \
+          .opt = MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT,  \
+          .val.auth_digest_nonce_timeout = (timeout) \
         } \
         MHD_RESTORE_WARN_COMPOUND_LITERALS_
 /**
  * Default maximum nc (nonce count) value used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * If followed by zero value the value is silently ignored.
  * @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
  * @param max_nc FIXME
  * @return structure with the requested setting
  */
-#  define MHD_D_OPTION_DAUTH_DEF_MAX_NC(max_nc) \
+#  define MHD_D_OPTION_AUTH_DIGEST_DEF_MAX_NC(max_nc) \
         MHD_NOWARN_COMPOUND_LITERALS_ \
           (const struct MHD_DaemonOptionAndValue) \
         { \
-          .opt = MHD_D_O_DAUTH_DEF_MAX_NC,  \
-          .val.dauth_def_max_nc = (max_nc) \
+          .opt = MHD_D_O_AUTH_DIGEST_DEF_MAX_NC,  \
+          .val.auth_digest_def_max_nc = (max_nc) \
         } \
         MHD_RESTORE_WARN_COMPOUND_LITERALS_
 
@@ -2268,6 +2239,7 @@ MHD_D_OPTION_NOTIFY_STREAM (
 /**
  * Set strong random data to be used by MHD.
  * Currently the data is only needed for Digest Auth module.
+ * Daemon support for Digest Auth is enabled automatically if this option is used.
  * The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four.
  * Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security.
  * @param buf_size the size of the buffer
@@ -2293,62 +2265,40 @@ MHD_D_OPTION_RANDOM_ENTROPY (
 /**
  * Specify the size of the internal hash map array that tracks generated digest nonces usage.
  * When the size of the map is too small then need to handle concurrent DAuth requests, a lot of stale nonce results will be produced.
- * By default the size is 8 bytes (very small).
+ * By default the size is 1000 entries.
  * @param size the size of the map array
  * @return structure with the requested setting
  */
 static MHD_INLINE struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_MAP_SIZE (
+MHD_D_OPTION_AUTH_DIGEST_MAP_SIZE (
   size_t size
   )
 {
   struct MHD_DaemonOptionAndValue opt_val;
 
-  opt_val.opt = MHD_D_O_DAUTH_MAP_SIZE;
-  opt_val.val.dauth_map_size = size;
-
-  return opt_val;
-}
-
-
-/**
- * Control the scope of validity of MHD-generated nonces.
- * This regulates how nonces are generated and how nonces are checked by #MHD_digest_auth_check() and similar functions.
- * This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values.
- * When this option is not used then default value is #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
- * @param bind_type FIXME
- * @return structure with the requested setting
- */
-static MHD_INLINE struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_NONCE_BIND_TYPE (
-  enum MHD_DaemonOptionValueDAuthBindNonce bind_type
-  )
-{
-  struct MHD_DaemonOptionAndValue opt_val;
-
-  opt_val.opt = MHD_D_O_DAUTH_NONCE_BIND_TYPE;
-  opt_val.val.dauth_nonce_bind_type = bind_type;
+  opt_val.opt = MHD_D_O_AUTH_DIGEST_MAP_SIZE;
+  opt_val.val.auth_digest_map_size = size;
 
   return opt_val;
 }
 
 
 /**
- * Default nonce timeout value (in seconds) used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * Nonce validity time (in seconds) used for Digest Auth.
+ * If followed by zero value the value is silently ignored.
  * @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
  * @param timeout FIXME
  * @return structure with the requested setting
  */
 static MHD_INLINE struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_DEF_NONCE_TIMEOUT (
+MHD_D_OPTION_AUTH_DIGEST_NONCE_TIMEOUT (
   unsigned int timeout
   )
 {
   struct MHD_DaemonOptionAndValue opt_val;
 
-  opt_val.opt = MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT;
-  opt_val.val.dauth_def_nonce_timeout = timeout;
+  opt_val.opt = MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT;
+  opt_val.val.auth_digest_nonce_timeout = timeout;
 
   return opt_val;
 }
@@ -2356,20 +2306,20 @@ MHD_D_OPTION_DAUTH_DEF_NONCE_TIMEOUT (
 
 /**
  * Default maximum nc (nonce count) value used for Digest Auth.
- * Silently ignored if followed by zero value.
+ * If followed by zero value the value is silently ignored.
  * @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest()
  * @param max_nc FIXME
  * @return structure with the requested setting
  */
 static MHD_INLINE struct MHD_DaemonOptionAndValue
-MHD_D_OPTION_DAUTH_DEF_MAX_NC (
+MHD_D_OPTION_AUTH_DIGEST_DEF_MAX_NC (
   uint_fast32_t max_nc
   )
 {
   struct MHD_DaemonOptionAndValue opt_val;
 
-  opt_val.opt = MHD_D_O_DAUTH_DEF_MAX_NC;
-  opt_val.val.dauth_def_max_nc = max_nc;
+  opt_val.opt = MHD_D_O_AUTH_DIGEST_DEF_MAX_NC;
+  opt_val.val.auth_digest_def_max_nc = max_nc;
 
   return opt_val;
 }

+ 151 - 108
src/include/microhttpd2_main.h.in

@@ -2506,7 +2506,7 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthAlgo
 {
   /**
    * Unknown or wrong algorithm type.
-   * Used in struct MHD_DigestAuthInfo to indicate client value that
+   * Used in struct MHD_AuthDigestInfo to indicate client value that
    * cannot by identified.
    */
   MHD_DIGEST_AUTH_ALGO_INVALID = 0
@@ -2553,12 +2553,12 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthAlgo
 
 
 /**
- * Get digest size for specified algorithm.
+ * Get digest size in bytes for specified algorithm.
  *
  * The size of the digest specifies the size of the userhash, userdigest
  * and other parameters which size depends on used hash algorithm.
  * @param algo the algorithm to check
- * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
+ * @return the size (in bytes) of the digest (either #MHD_MD5_DIGEST_SIZE or
  *         #MHD_SHA256_DIGEST_SIZE/MHD_SHA512_256_DIGEST_SIZE)
  *         or zero if the input value is not supported or not valid
  * @sa #MHD_digest_auth_calc_userdigest()
@@ -2722,15 +2722,18 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthMultiAlgo
  *                          upon return
  * @param bin_buf_size the size of the @a userhash_bin buffer, must be
  *                     at least #MHD_digest_get_hash_size() bytes long
- * @return MHD_SC_OK on success,
- *         error code otherwise
+ * @return #MHD_SC_OK on success,
+ *         #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ *         #MHD_SC_HASH_FAILED if hashing failed,
+ *         #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ *                                                unknown or unsupported.
  * @sa #MHD_digest_auth_calc_userhash_hex()
  * @ingroup authentication
  */
 MHD_EXTERN_ enum MHD_StatusCode
 MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo algo,
-                               const char *username,
-                               const char *realm,
+                               const char *MHD_RESTRICT username,
+                               const char *MHD_RESTRICT realm,
                                size_t bin_buf_size,
                                void *MHD_RESTRICT userhash_bin)
 MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_ (2)
@@ -2768,17 +2771,20 @@ MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4);
  * @param[out] userhash_hex the output buffer for userhash as hex string;
  *                          if this function succeeds, then this buffer has
  *                          #MHD_digest_get_hash_size()*2 chars long
- *                          userhash zero-terminated string
- * @return MHD_SC_OK on success,
- *         error code otherwise
+ *                          userhash string plus one zero-termination char
+ * @return #MHD_SC_OK on success,
+ *         #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ *         #MHD_SC_HASH_FAILED if hashing failed,
+ *         #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ *                                                unknown or unsupported.
  * @sa #MHD_digest_auth_calc_userhash()
  * @ingroup authentication
  */
 MHD_EXTERN_ enum MHD_StatusCode
 MHD_digest_auth_calc_userhash_hex (
   enum MHD_DigestAuthAlgo algo,
-  const char *username,
-  const char *realm,
+  const char *MHD_RESTRICT username,
+  const char *MHD_RESTRICT realm,
   size_t hex_buf_size,
   char userhash_hex[MHD_FN_PAR_DYN_ARR_SIZE_ (hex_buf_size)])
 MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_ (2)
@@ -2791,7 +2797,7 @@ MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4);
  * Values are sorted so simplified checks could be used.
  * For example:
  * * (value <= MHD_DIGEST_AUTH_UNAME_TYPE_INVALID) is true if no valid username
- *   is provided by the client
+ *   is provided by the client (not used currently)
  * * (value >= MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH) is true if username is
  *   provided in any form
  * * (value >= MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD) is true if username is
@@ -2801,7 +2807,8 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthUsernameType
 {
   /**
    * No username parameter in in Digest Authorization header.
-   * This should be treated as an error.
+   * Not used currently. Value #MHD_SC_REQ_AUTH_DATA_BROKEN is returned
+   * by #MHD_request_get_info_dynamic_sz() if the request has no username.
    */
   MHD_DIGEST_AUTH_UNAME_TYPE_MISSING = 0
   ,
@@ -2828,10 +2835,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthUsernameType
   /**
    * The invalid combination of username parameters are used by client.
    * Either:
-   * * both 'username' and 'username*' are used
-   * * 'username*' is used with 'userhash=true'
-   * * 'username*' used with invalid extended notation
-   * * 'username' is not hexadecimal string, while 'userhash' set to 'true'
+   * + both 'username' and 'username*' are used
+   * + 'username*' is used with 'userhash=true'
+   * + 'username*' used with invalid extended notation
+   * + 'username' is not hexadecimal string, while 'userhash' set to 'true'
+   * Not used currently. Value #MHD_SC_REQ_AUTH_DATA_BROKEN is returned
+   * by #MHD_request_get_info_dynamic_sz() if the request has broken username.
    */
   MHD_DIGEST_AUTH_UNAME_TYPE_INVALID = (1u << 0)
 };
@@ -2843,7 +2852,7 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthQOP
 {
   /**
    * Invalid/unknown QOP.
-   * Used in struct MHD_DigestAuthInfo to indicate client value that
+   * Used in struct MHD_AuthDigestInfo to indicate client value that
    * cannot by identified.
    */
   MHD_DIGEST_AUTH_QOP_INVALID = 0
@@ -2923,16 +2932,52 @@ enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_DigestAuthMultiQOP
 };
 
 /**
- * The invalid value of 'nc' parameter in client Digest Authorization header.
+ * The type of 'nc' (nonce count) value provided in the request
  */
-#define MHD_DIGEST_AUTH_INVALID_NC_VALUE        (0)
+enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthNC
+{
+  /**
+   * Readable hexdecimal non-zero number.
+   * The decoded value is placed in @a nc member of struct MHD_AuthDigestInfo
+   */
+  MHD_DIGEST_AUTH_NC_NUMBER = 1
+  ,
+  /**
+   * Readable zero number.
+   * Compliant clients should not use such values.
+   * Can be treated as invalid request.
+   */
+  MHD_DIGEST_AUTH_NC_ZERO = 2
+  ,
+  /**
+   * 'nc' value is not provided by the client.
+   * Unless old RFC 2069 mode is allowed, this should be treated as invalid
+   * request.
+   */
+  MHD_DIGEST_AUTH_NC_NONE = 3
+  ,
+  /**
+   * 'nc' value is too long to be decoded.
+   * Compliant clients should not use such values.
+   * Can be treated as invalid request.
+   */
+  MHD_DIGEST_AUTH_NC_TOO_LONG = 4
+  ,
+  /**
+   * 'nc' value is too large for uint32_t.
+   * Compliant clients should not use such values.
+   * Can be treated as request with a stale nonce or as invalid request.
+   */
+  MHD_DIGEST_AUTH_NC_TOO_LARGE = 5
+};
+
 
 /**
  * Information from Digest Authorization client's header.
  *
- * @see #MHD_REQUEST_INFO_DYNAMIC_DAUTH_REQ_INFO
+ * @see #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO
  */
-struct MHD_DigestAuthInfo
+struct MHD_AuthDigestInfo
 {
   /**
    * The algorithm as defined by client.
@@ -2977,7 +3022,7 @@ struct MHD_DigestAuthInfo
    *          data.
    * @sa #MHD_digest_auth_calc_userhash()
    */
-  uint8_t *userhash_bin;
+  const uint8_t *userhash_bin;
 
   /**
    * The size of the data pointed by @a userhash_bin.
@@ -3013,13 +3058,17 @@ struct MHD_DigestAuthInfo
   size_t cnonce_len;
 
   /**
-   * The nc parameter value.
+   * The type of 'nc' (nonce count) value provided in the request.
+   */
+  enum MHD_DigestAuthNC nc_type;
+
+  /**
+   * The nc (nonce count) parameter value.
    * Can be used by application to limit the number of nonce re-uses. If @a nc
    * is higher than application wants to allow, then "auth required" response
    * with 'stale=true' could be used to force client to retry with the fresh
    * 'nonce'.
-   * If not specified by client or does not have hexadecimal digits only, the
-   * value is #MHD_DIGEST_AUTH_INVALID_NC_VALUE.
+   * Set to zero when @a nc_type is not set to #MHD_DIGEST_AUTH_NC_NUMBER.
    */
   uint_fast32_t nc;
 };
@@ -3028,9 +3077,9 @@ struct MHD_DigestAuthInfo
 /**
  * Information from Digest Authorization client's header.
  *
- * @see #MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO
+ * @see #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME
  */
-struct MHD_DigestAuthUsernameInfo
+struct MHD_AuthDigestUsernameInfo
 {
   /**
    * The algorithm as defined by client.
@@ -3041,7 +3090,10 @@ struct MHD_DigestAuthUsernameInfo
   /**
    * The type of username used by client.
    * The 'invalid' and 'missing' types are not used in this structure,
-   * instead NULL is returned for #MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO.
+   * instead #MHD_SC_REQ_AUTH_DATA_BROKEN is returned when
+   * #MHD_request_get_info_dynamic_sz() called with
+   * #MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME and the request has
+   * a broken username data.
    */
   enum MHD_DigestAuthUsernameType uname_type;
 
@@ -3056,7 +3108,7 @@ struct MHD_DigestAuthUsernameInfo
    * The buffer pointed by the @a username becomes invalid when a response
    * for the requested is provided (or request is aborted).
    */
-  struct MHD_String username;
+  struct MHD_StringNullable username;
 
   /**
    * The userhash string.
@@ -3067,7 +3119,7 @@ struct MHD_DigestAuthUsernameInfo
    * for the requested is provided (or request is aborted).
    * @sa #MHD_digest_auth_calc_userhash_hex()
    */
-  struct MHD_String userhash_hex;
+  struct MHD_StringNullable userhash_hex;
 
   /**
    * The userhash decoded to binary form.
@@ -3083,7 +3135,7 @@ struct MHD_DigestAuthUsernameInfo
    *          data.
    * @sa #MHD_digest_auth_calc_userhash()
    */
-  uint8_t *userhash_bin;
+  const uint8_t *userhash_bin;
 };
 
 
@@ -3101,45 +3153,66 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
   ,
   /**
    * General error, like "out of memory".
+   * Authentication may be valid, but cannot be checked.
    */
   MHD_DAUTH_ERROR = 0
   ,
   /**
-   * No "Authorization" header or wrong format of the header.
-   * Also may be returned if required parameters in client Authorisation header
-   * are missing or broken (in invalid format).
+   * No "Authorization" header for Digest Authentication.
    */
-  MHD_DAUTH_WRONG_HEADER = -1
+  MHD_DAUTH_HEADER_MISSING = -1
+  ,
+  /**
+   * Wrong format of the header.
+   * Also returned if required parameters in Authorization header are missing
+   * or broken (in invalid format).
+   */
+  MHD_DAUTH_HEADER_BROKEN = -9
+  ,
+  /**
+   * Unsupported algorithm.
+   */
+  MHD_DAUTH_UNSUPPORTED_ALGO = -10
+  ,
+  /**
+   * Unsupported 'qop'.
+   */
+  MHD_DAUTH_UNSUPPORTED_QOP = -11
+  ,
+  /**
+   * Incorrect userdigest size.
+   */
+  MHD_DAUTH_INVALID_USERDIGEST_SIZE = -15
   ,
   /**
    * Wrong 'username'.
    */
-  MHD_DAUTH_WRONG_USERNAME = -2
+  MHD_DAUTH_WRONG_USERNAME = -17
   ,
   /**
    * Wrong 'realm'.
    */
-  MHD_DAUTH_WRONG_REALM = -3
+  MHD_DAUTH_WRONG_REALM = -18
   ,
   /**
    * Wrong 'URI' (or URI parameters).
    */
-  MHD_DAUTH_WRONG_URI = -4
+  MHD_DAUTH_WRONG_URI = -19
   ,
   /**
    * Wrong 'qop'.
    */
-  MHD_DAUTH_WRONG_QOP = -5
+  MHD_DAUTH_WRONG_QOP = -20
   ,
   /**
    * Wrong 'algorithm'.
    */
-  MHD_DAUTH_WRONG_ALGO = -6
+  MHD_DAUTH_WRONG_ALGO = -21
   ,
   /**
    * Too large (>64 KiB) Authorization parameter value.
    */
-  MHD_DAUTH_TOO_LARGE = -15
+  MHD_DAUTH_TOO_LARGE = -22
   ,
   /* The different form of naming is intentionally used for the results below,
    * as they are more important */
@@ -3149,22 +3222,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
    * username and password to get the fresh 'nonce'.
    * The validity of the 'nonce' may be not checked.
    */
-  MHD_DAUTH_NONCE_STALE = -17
-  ,
-  /**
-   * The 'nonce' was generated by MHD for other conditions.
-   * This value is only returned if #MHD_D_O_DAUTH_NONCE_BIND_TYPE is set
-   * to anything other than #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_NONE.
-   * The interpretation of this code could be different. For example, if
-   * #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_URI is set and client just used
-   * the same 'nonce' for another URI, the code could be handled as
-   * #MHD_DAUTH_NONCE_STALE as RFCs allow nonces re-using for other URIs
-   * in the same "protection space".
-   * However, if only #MHD_D_OPTION_VALUE_DAUTH_BIND_NONCE_CLIENT_IP bit is set
-   * and it is know that clients have fixed IP addresses, this return code could
-   * be handled like #MHD_DAUTH_NONCE_WRONG.
-   */
-  MHD_DAUTH_NONCE_OTHER_COND = -18
+  MHD_DAUTH_NONCE_STALE = -25
   ,
   /**
    * The 'nonce' is wrong. May indicate an attack attempt.
@@ -3172,7 +3230,8 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
   MHD_DAUTH_NONCE_WRONG = -33
   ,
   /**
-   * The 'response' is wrong. May indicate an attack attempt.
+   * The 'response' is wrong. May indicate a wrong password used or
+   * an attack attempt.
    */
   MHD_DAUTH_RESPONSE_WRONG = -34
 };
@@ -3194,9 +3253,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
  * @param username the username to be authenticated, must be in clear text
  *                 even if userhash is used by the client
  * @param password the password matching the @a username (and the @a realm)
- * @param nonce_timeout the period of seconds since nonce generation, when
- *                      the nonce is recognised as valid and not stale;
- *                      if zero is specified then daemon default value is used.
  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
  *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
  *               returned;
@@ -3209,18 +3265,15 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_DigestAuthResult
  * @ingroup authentication
  */
 MHD_EXTERN_ enum MHD_DigestAuthResult
-MHD_digest_auth_check (struct MHD_Request *request,
-                       const char *realm,
-                       const char *username,
-                       const char *password,
-                       unsigned int nonce_timeout,
+MHD_digest_auth_check (struct MHD_Request *MHD_RESTRICT request,
+                       const char *MHD_RESTRICT realm,
+                       const char *MHD_RESTRICT username,
+                       const char *MHD_RESTRICT password,
                        uint_fast32_t max_nc,
                        enum MHD_DigestAuthMultiQOP mqop,
                        enum MHD_DigestAuthMultiAlgo malgo)
-MHD_FN_PAR_NONNULL_ (1)
-MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
-MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3)
-MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
+MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4);
 
 
 /**
@@ -3249,17 +3302,20 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
  *                            #MHD_digest_get_hash_size() bytes of
  *                            userdigest upon return
  * @return #MHD_SC_OK on success,
- *         error code otherwise.
+ *         #MHD_SC_OUT_BUFF_TOO_SMALL if @a bin_buf_size is too small,
+ *         #MHD_SC_HASH_FAILED if hashing failed,
+ *         #MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED if requested @a algo is
+ *                                                unknown or unsupported.
  * @sa #MHD_digest_auth_check_digest()
  * @ingroup authentication
  */
 MHD_EXTERN_ enum MHD_StatusCode
 MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo algo,
-                                 const char *username,
-                                 const char *realm,
-                                 const char *password,
+                                 const char *MHD_RESTRICT username,
+                                 const char *MHD_RESTRICT realm,
+                                 const char *MHD_RESTRICT password,
                                  size_t bin_buf_size,
-                                 void *userdigest_bin)
+                                 void *MHD_RESTRICT userdigest_bin)
 MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
 MHD_FN_PAR_CSTR_ (2)
 MHD_FN_PAR_CSTR_ (3)
@@ -3283,45 +3339,41 @@ MHD_FN_PAR_OUT_SIZE_ (6,5);
  * @param realm the realm for authorization of the client
  * @param username the username 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",
- *                   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_SHA512_256_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;
- *                      if zero is specified then daemon default value is used.
+ * @param userdigest the precalculated binary hash of the string
+ *                   "username:realm:password",
+ *                   see #MHD_digest_auth_calc_userdigest()
  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
  *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
  *               returned;
  *               if zero is specified then daemon default value is used.
  * @param mqop the QOP to use
  * @param malgo digest algorithms allowed to use, fail if algorithm used
- *               by the client is not allowed by this parameter;
- *               more than one base algorithms (MD5, SHA-256, SHA-512/256)
- *               cannot be used at the same time for this function
- *               as @a userdigest must match specified algorithm
+ *              by the client is not allowed by this parameter;
+ *              more than one base algorithms (MD5, SHA-256, SHA-512/256)
+ *              cannot be used at the same time for this function
+ *              as @a userdigest must match specified algorithm
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
  * @sa #MHD_digest_auth_calc_userdigest()
  * @ingroup authentication
  */
 MHD_EXTERN_ enum MHD_DigestAuthResult
-MHD_digest_auth_check_digest (struct MHD_Request *request,
-                              const char *realm,
-                              const char *username,
-                              const void *userdigest,
+MHD_digest_auth_check_digest (struct MHD_Request *MHD_RESTRICT request,
+                              const char *MHD_RESTRICT realm,
+                              const char *MHD_RESTRICT username,
                               size_t userdigest_size,
-                              unsigned int nonce_timeout,
+                              const void *MHD_RESTRICT userdigest,
                               uint_fast32_t max_nc,
                               enum MHD_DigestAuthMultiQOP mqop,
                               enum MHD_DigestAuthMultiAlgo malgo)
 MHD_FN_PAR_NONNULL_ALL_
 MHD_FN_PAR_CSTR_ (2)
 MHD_FN_PAR_CSTR_ (3)
-MHD_FN_PAR_CSTR_ (4);
+MHD_FN_PAR_IN_SIZE_ (5, 4);
 
 
 /**
@@ -3648,15 +3700,6 @@ MHD_STATIC_INLINE_END_
 #endif /* ! MHD_NO_STATIC_INLINE */
 
 
-/**
- * Constant to indicate that the nonce of the provided
- * authentication code was wrong.
- * Used as return code by #MHD_digest_auth_check(),
- * #MHD_digest_auth_check_digest()
- * @ingroup authentication
- */
-#define MHD_INVALID_NONCE -1
-
 /**
  * Add Basic Authentication "challenge" to the response.
  *
@@ -5336,9 +5379,9 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_RequestInfoDynamicType
    * username is provided or the format of the username parameter is broken.
    * Pointers in the returned structure (if any) are valid until response
    * is provided for the request.
-   * The result is placed in @a v_dauth_username member.
+   * The result is placed in @a v_auth_digest_uname member.
    */
-  MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO = 41
+  MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME = 41
   ,
   /**
    * Returns pointer to information about digest auth in client request.
@@ -5346,9 +5389,9 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_RequestInfoDynamicType
    * the client or the format of the digest auth header is broken.
    * Pointers in the returned structure (if any) are valid until response
    * is provided for the request.
-   * The result is placed in @a v_dauth_info member.
+   * The result is placed in @a v_auth_digest_info member.
    */
-  MHD_REQUEST_INFO_DYNAMIC_DAUTH_REQ_INFO = 42
+  MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO = 42
   ,
   /**
    * Returns information about Basic Authentication credentials in the request.
@@ -5402,12 +5445,12 @@ union MHD_RequestInfoDynamicData
   /**
    * The information about client provided username for digest auth
    */
-  const struct MHD_DigestAuthUsernameInfo *v_dauth_username;
+  const struct MHD_AuthDigestUsernameInfo *v_auth_digest_uname;
 
   /**
    * The information about client's digest auth
    */
-  const struct MHD_DigestAuthInfo *v_dauth_info;
+  const struct MHD_AuthDigestInfo *v_auth_digest_info;
 
   /**
    * The username and password provided by the client's basic auth header.

+ 25 - 1
src/include/microhttpd2_preamble.h.in

@@ -1230,6 +1230,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_REPLY_FILE_READ_ERROR = 50232
   ,
+  /**
+   * Failed to generate the nonce for the Digest Auth.
+   */
+  MHD_SC_REPLY_NONCE_ERROR = 50233
+  ,
   /**
    * Failed to allocate memory in connection's pool for the reply.
    */
@@ -1379,6 +1384,14 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_TLS_CONNECTION_HANDSHAKED_FAILED = 51220
   ,
+  /**
+   * Hashing failed.
+   * Internal hashing function can never fail (and this code is never returned
+   * for them). External hashing function (like TLS backend-based) may fail
+   * for various reasons, like failure of hardware acccelerated hashing.
+   */
+  MHD_SC_HASH_FAILED = 51260
+  ,
   /**
    * Something wrong in the internal MHD logic.
    * This error should be never returned if MHD works as expected.
@@ -1592,6 +1605,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_UPGRADED_HANDLE_INVALID = 60161
   ,
+  /**
+   * The provided output buffer is too small.
+   */
+  MHD_SC_OUT_BUFF_TOO_SMALL = 60180
+  ,
   /**
    * The requested type of information is not recognised.
    */
@@ -1628,7 +1646,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
   MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED = 60240
   ,
   /**
-   * The the Digest Auth QOP value is unknown or not supported.
+   * The Digest Auth QOP value is unknown or not supported.
    */
   MHD_SC_AUTH_DIGEST_QOP_NOT_SUPPORTED = 60241
 };
@@ -3892,6 +3910,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode
    */
   MHD_REQUEST_ENDED_FILE_ERROR = 51
   ,
+  /**
+   * The request was aborted due to error generating valid nonce for Digest Auth
+   * @ingroup request
+   */
+  MHD_REQUEST_ENDED_NONCE_ERROR = 52
+  ,
   /**
    * Closing the session since MHD is being shut down.
    * @ingroup request

+ 38 - 1
src/mhd2/Makefile.am

@@ -110,9 +110,46 @@ auth_basic_OPTSOURCES = \
   auth_basic.c              auth_basic.h \
   response_auth_basic.c
 
+if MHD_MD5_EXTR
+md5_OPTSOURCES = \
+  md5_ext.c                 md5_ext.h \
+  mhd_md5.h
+else
+md5_OPTSOURCES = \
+  md5_int.c                 md5_int.h \
+  mhd_md5.h
+endif
+
+if MHD_SHA256_EXTR
+sha256_OPTSOURCES = \
+  sha256_ext.c              sha256_ext.h \
+  mhd_sha256.h
+else
+sha256_OPTSOURCES = \
+  sha256_int.c              sha256_int.h \
+  mhd_sha256.h
+endif
+
+sha512_256_OPTSOURCES = \
+  sha512_256_int.c          sha512_256_int.h \
+  mhd_sha512_256.h
+
 auth_digest_OPTSOURCES = \
   mhd_digest_auth_data.h    mhd_auth_digest_hdr.h \
-  response_auth_digest.c    response_auth_digest.h
+  response_auth_digest.c    response_auth_digest.h \
+  auth_digest.c             auth_digest.h
+
+if MHD_SUPPORT_MD5
+auth_digest_OPTSOURCES += $(md5_OPTSOURCES)
+endif
+
+if MHD_SUPPORT_SHA256
+auth_digest_OPTSOURCES += $(sha256_OPTSOURCES)
+endif
+
+if MHD_SUPPORT_SHA512_256
+auth_digest_OPTSOURCES += $(sha512_256_OPTSOURCES)
+endif
 
 upgrade_OPTSOURCES = \
   mhd_upgrade.h \

+ 18 - 0
src/mhd2/action.c

@@ -26,8 +26,12 @@
 
 #include "mhd_sys_options.h"
 
+#include "mhd_cntnr_ptr.h"
+#include "mhd_connection.h"
+#include "mhd_daemon.h"
 #include "mhd_action.h"
 #include "mhd_request.h"
+#include "mhd_response.h"
 
 #include "daemon_logger.h"
 
@@ -71,6 +75,13 @@ MHD_action_from_response (struct MHD_Request *request,
     mhd_response_dec_use_count (response);
     return (const struct MHD_Action *) NULL;
   }
+  if (mhd_RESP_HAD_AUTH_DIGEST (response) &&
+      ! mhd_D_HAS_AUTH_DIGEST ( \
+        mhd_CNTNR_CPTR (request, struct MHD_Connection, rq)->daemon))
+  {
+    mhd_response_dec_use_count (response);
+    return (const struct MHD_Action *) NULL;
+  }
 
   head_act->act = mhd_ACTION_RESPONSE;
   head_act->data.response = response;
@@ -262,6 +273,13 @@ MHD_upload_action_from_response (struct MHD_Request *request,
     mhd_response_dec_use_count (response);
     return (const struct MHD_UploadAction *) NULL;
   }
+  if (mhd_RESP_HAD_AUTH_DIGEST (response) &&
+      ! mhd_D_HAS_AUTH_DIGEST ( \
+        mhd_CNTNR_CPTR (request, struct MHD_Connection, rq)->daemon))
+  {
+    mhd_response_dec_use_count (response);
+    return (const struct MHD_UploadAction *) NULL;
+  }
 
   upl_act->act = mhd_UPLOAD_ACTION_RESPONSE;
   upl_act->data.response = response;

+ 3386 - 0
src/mhd2/auth_digest.c

@@ -0,0 +1,3386 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2014-2025 Evgeny Grin (Karlson2k)
+  Copyright (C) 2010, 2011, 2012, 2015, 2018 Christian Grothoff
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/auth_digest.c
+ * @brief  The implementation of the Digest Authorization internal functions
+ * @author Karlson2k (Evgeny Grin)
+ * Based on the MHD v0.xx code by Amr Ali, Matthieu Speder, Christian Grothoff,
+ * Dirk Brinkmeier and Evgeny Grin.
+ */
+
+#include "mhd_sys_options.h"
+
+#include "mhd_digest_auth_data.h"
+
+#include "mhd_assert.h"
+#include "mhd_unreachable.h"
+
+#include "mhd_str_macros.h"
+#include "mhd_bithelpers.h"
+#include "mhd_arr_num_elems.h"
+#include "mhd_cntnr_ptr.h"
+#include "mhd_limits.h"
+
+#include "mhd_str_types.h"
+#include "mhd_buffer.h"
+#include "mhd_daemon.h"
+#include "mhd_request.h"
+#include "mhd_connection.h"
+
+#ifdef MHD_SUPPORT_SHA512_256
+#  include "mhd_sha512_256.h"
+#endif /* MHD_SUPPORT_SHA512_256 */
+#ifdef MHD_SUPPORT_SHA256
+#  include "mhd_sha256.h"
+#endif
+#ifdef MHD_SUPPORT_MD5
+#  include "mhd_md5.h"
+#endif
+
+#include "mhd_str.h"
+#include "mhd_mono_clock.h"
+#include "mhd_atomic_counter.h"
+#include "mhd_locks.h"
+
+#include "request_auth_get.h"
+#include "daemon_funcs.h"
+#include "stream_funcs.h"
+#include "stream_process_request.h"
+
+#include "auth_digest.h"
+
+/*
+ * The maximum size of the hash digest, in bytes
+ */
+#if defined(MHD_SUPPORT_SHA512_256)
+#  define mhd_MAX_DIGEST mhd_SHA512_256_DIGEST_SIZE
+#elif defined(MHD_SUPPORT_SHA256)
+#  define mhd_MAX_DIGEST mhd_SHA256_DIGEST_SIZE
+#else
+#  define mhd_MAX_DIGEST mhd_MD5_DIGEST_SIZE
+#endif
+
+/**
+ * MD5 algorithm identifier for Digest Auth headers
+ */
+#define mhd_MD5_TOKEN "MD5"
+
+/**
+ * SHA-256 algorithm identifier for Digest Auth headers
+ */
+#define mhd_SHA256_TOKEN "SHA-256"
+
+/**
+ * SHA-512/256 algorithm for Digest Auth headers.
+ */
+#define mhd_SHA512_256_TOKEN "SHA-512-256"
+
+/**
+ * The suffix token for "session" algorithms for Digest Auth headers.
+ */
+#define mhd_SESS_TOKEN "-sess"
+
+/**
+ * The "auth" token for QOP for Digest Auth headers.
+ */
+#define mhd_TOKEN_AUTH "auth"
+
+/**
+ * The "auth-int" token for QOP for Digest Auth headers.
+ */
+#define mhd_TOKEN_AUTH_INT "auth-int"
+
+
+/**
+ * The required prefix of parameter with the extended notation
+ */
+#define mhd_DAUTH_EXT_PARAM_PREFIX "UTF-8'"
+
+/**
+ * The minimal size of the prefix for parameter with the extended notation
+ */
+#define mhd_DAUTH_EXT_PARAM_MIN_LEN \
+        mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX "'")
+
+/**
+ * The maximum supported size for Digest Auth parameters, like "realm",
+ * "username" etc.
+ * This limitation is used only for quoted parameters.
+ * Parameters without quoted backslash character will be processed as long
+ * as they fit connection memory pool (buffer) size.
+ */
+#define mhd_AUTH_DIGEST_MAX_PARAM_SIZE (65535)
+
+/**
+ * Parameter of request's Digest Authorization header
+ */
+struct mhd_RqDAuthParam
+{
+  /**
+   * The string with length, NOT zero-terminated
+   */
+  struct MHD_StringNullable value;
+  /**
+   * True if string must be "unquoted" before processing.
+   * This member is false if the string is used in DQUOTE marks, but no
+   * backslash-escape is used in the string.
+   */
+  bool quoted;
+};
+
+/**
+ * Client's Digest Authorization header parameters
+ */
+struct mhd_AuthDigesReqParams
+{
+  struct mhd_RqDAuthParam nonce;
+  struct mhd_RqDAuthParam opaque;
+  struct mhd_RqDAuthParam response;
+  struct mhd_RqDAuthParam username;
+  struct mhd_RqDAuthParam username_ext;
+  struct mhd_RqDAuthParam realm;
+  struct mhd_RqDAuthParam uri;
+  /* The raw QOP value, used in the 'response' calculation */
+  struct mhd_RqDAuthParam qop_raw;
+  struct mhd_RqDAuthParam cnonce;
+  struct mhd_RqDAuthParam nc;
+
+  /* Decoded values are below */
+  bool userhash; /* True if 'userhash' parameter has value 'true'. */
+  enum MHD_DigestAuthAlgo algo;
+  enum MHD_DigestAuthQOP qop;
+};
+
+/**
+ * Digest context data
+ */
+union DigestCtx
+{
+#ifdef MHD_SUPPORT_SHA512_256
+  struct mhd_Sha512_256Ctx sha512_256_ctx;
+#endif /* MHD_SUPPORT_SHA512_256 */
+#ifdef MHD_SUPPORT_SHA256
+  struct mhd_Sha256Ctx sha256_ctx;
+#endif /* MHD_SUPPORT_SHA256 */
+#ifdef MHD_SUPPORT_MD5
+  struct mhd_Md5Ctx md5_ctx;
+#endif /* MHD_SUPPORT_MD5 */
+};
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
+
+/**
+ * Generate simple hash.
+ * Very limited avalanche effect. To be used mainly for the table slot choice.
+ * @param data_size the size of the data to hash
+ * @param data the data to hash
+ * @return the hash value
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (2, 1) uint_fast64_t
+simple_hash (size_t data_size,
+             const uint8_t *restrict data)
+{
+  static const uint_fast64_t c[] = { /* Some fractional parts of Euler's number */
+    UINT64_C (0xCC64D3484C3475A1),
+    UINT64_C (0xCF4DEBCB9ED801F2),
+    UINT64_C (0x0C8737A803CF46AD),
+    UINT64_C (0x294C9E0E0F9F14AB),
+    UINT64_C (0xAD786D855D4EBB1A)
+  };
+  uint_fast64_t res;
+  size_t i;
+
+  res = UINT64_C (0x8316A8FE31A2228E); /* Some fractional part of Pi */
+  i = 0;
+  while (1)
+  {
+    uint_fast64_t a = 0;
+
+    if (8 <= data_size)
+      memcpy (&a, data, 8);
+    else
+      memcpy (&a, data, data_size);
+    a ^= c[(i++) % mhd_ARR_NUM_ELEMS (c)];
+    a = (uint_fast64_t) mhd_ROTR64 ((uint64_t) a, \
+                                    (int) (res >> 58u));
+    res ^= a;
+    if (8 > data_size)
+      break;
+    data_size -= 8;
+    data += 8;
+  }
+  return res;
+}
+
+
+/**
+ * Find index of the provided nonce in the nonces table
+ * @param nonce the nonce to use
+ * @param arr_size the size of the nonces table
+ * @return the index
+ */
+static MHD_FN_PAR_NONNULL_ALL_ size_t
+nonce_to_index (const uint8_t nonce[mhd_AUTH_DIGEST_NONCE_BIN_SIZE],
+                size_t arr_size)
+{
+  uint_fast64_t hash;
+  hash = simple_hash (mhd_AUTH_DIGEST_NONCE_BIN_SIZE,
+                      nonce);
+  if (arr_size == (arr_size & UINT32_C (0xFFFFFFFF)))
+  { /* 'arr_size' <=32-bit */
+    hash = (hash ^ (hash >> 32)) & UINT32_C (0xFFFFFFFF); /* Fold hash */
+    if (arr_size == (arr_size & UINT16_C (0xFFFF)))
+    { /* 'arr_size' <=16-bit */
+      hash = (hash ^ (hash >> 16)) & UINT16_C (0xFFFF); /* Fold hash */
+      if (arr_size == (arr_size & 0xFFu))
+        hash = (hash ^ (hash >> 8)) & 0xFFu; /* 'arr_size' <=8-bit, fold hash */
+    }
+  }
+  return ((size_t) hash) % arr_size;
+}
+
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE
+
+
+/**
+ * Generate a new nonce
+ * @param d the daemon to use (must match @a c connection)
+ * @param c the connection to generate nonce for
+ * @param[out] out_buf the output buffer to pull full nonce, including
+ *                     "expiration" tail
+ * @param[out] expir the expiration mark, duplicated for convenience
+ * @return 'true' if succeed,
+ *         'false' if failed
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (3)
+MHD_FN_PAR_OUT_ (4) bool
+gen_new_nonce (struct MHD_Daemon *restrict d,
+               struct MHD_Connection *restrict c,
+               uint8_t out_buf[mhd_AUTH_DIGEST_NONCE_BIN_SIZE],
+               uint_fast32_t *restrict expir)
+{
+  uint_fast64_t expiration;
+  size_t gen_num;
+  union DigestCtx d_ctx;
+
+  mhd_assert (! mhd_D_HAS_MASTER (d)); /* only master daemon should be used */
+  mhd_assert (d == c->daemon);
+  mhd_assert (0 != d->auth_dg.cfg.nonce_tmout);
+
+  gen_num = mhd_atomic_counter_inc_wrap_get (&(d->auth_dg.num_gen_nonces));
+
+  expiration = mhd_monotonic_msec_counter ()
+               + d->auth_dg.cfg.nonce_tmout * (uint_fast64_t) 1000;
+
+  // TODO: replace with pure random number
+
+#if defined(MHD_SUPPORT_SHA512_256)
+  mhd_SHA512_256_init_one_time (&(d_ctx.sha512_256_ctx));
+  mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+                         d->auth_dg.entropy.size,
+                         (const void*) d->auth_dg.entropy.data);
+  mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+                         sizeof(gen_num),
+                         (const void*) &gen_num);
+  if (0 != c->sk.addr.size)
+    mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+                           c->sk.addr.size,
+                           (const void*) c->sk.addr.data);
+  mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+                         sizeof(expiration),
+                         (const void*) &expiration);
+  mhd_SHA512_256_finish_deinit (&(d_ctx.sha512_256_ctx), \
+                                out_buf);
+  if (mhd_SHA512_256_has_err (&(d_ctx.sha512_256_ctx)))
+    return false;
+#elif defined(MHD_SUPPORT_SHA256)
+  mhd_SHA256_init_one_time (&(d_ctx.sha256_ctx));
+  mhd_SHA256_update (&(d_ctx.sha256_ctx),
+                     d->auth_dg.entropy.size,
+                     (const void*) d->auth_dg.entropy.data);
+  mhd_SHA256_update (&(d_ctx.sha256_ctx),
+                     sizeof(gen_num),
+                     (const void*) &gen_num);
+  if (0 != c->sk.addr.size)
+    mhd_SHA256_update (&(d_ctx.sha256_ctx),
+                       c->sk.addr.size,
+                       (const void*) c->sk.addr.data);
+  mhd_SHA256_update (&(d_ctx.sha256_ctx),
+                     sizeof(expiration),
+                     (const void*) &expiration);
+  mhd_SHA256_finish_deinit (&(d_ctx.sha256_ctx), \
+                            out_buf);
+  if (mhd_SHA256_has_err (&(d_ctx.sha256_ctx)))
+    return false;
+#else  /* MHD_SUPPORT_MD5 */
+#ifndef MHD_SUPPORT_MD5
+#error At least one hashing algorithm must be enabled
+#endif
+  mhd_MD5_init_one_time (&(d_ctx.md5_ctx));
+  mhd_MD5_update (&(d_ctx.md5_ctx),
+                  d->auth_dg.entropy.size,
+                  (const void*) d->auth_dg.entropy.data);
+  mhd_MD5_update (&(d_ctx.md5_ctx),
+                  sizeof(gen_num),
+                  (const void*) &gen_num);
+  if (0 != c->sk.addr.size)
+    mhd_MD5_update (&(d_ctx.md5_ctx),
+                    c->sk.addr.size,
+                    (const void*) c->sk.addr.data);
+  mhd_MD5_update (&(d_ctx.md5_ctx),
+                  sizeof(expiration),
+                  (const void*) &expiration);
+  mhd_MD5_finish_deinit (&(d_ctx.md5_ctx), \
+                         out_buf);
+  if (mhd_MD5_has_err (&(d_ctx.md5_ctx)))
+    return false;
+
+  /* One more hash, for the second part */
+  gen_num = mhd_atomic_counter_inc_wrap_get (&(d->auth_dg.num_gen_nonces));
+
+  mhd_MD5_init_one_time (&(d_ctx.md5_ctx));
+  mhd_MD5_update (&(d_ctx.md5_ctx),
+                  d->auth_dg.entropy.size,
+                  (const void*) d->auth_dg.entropy.data);
+  mhd_MD5_update (&(d_ctx.md5_ctx),
+                  sizeof(gen_num),
+                  (const void*) &gen_num);
+  if (0 != c->sk.addr.size)
+    mhd_MD5_update (&(d_ctx.md5_ctx),
+                    c->sk.addr.size,
+                    (const void*) c->sk.addr.data);
+  mhd_MD5_update (&(d_ctx.md5_ctx),
+                  sizeof(expiration),
+                  (const void*) &expiration);
+  mhd_MD5_finish_deinit (&(d_ctx.md5_ctx), \
+                         out_buf + mhd_MD5_DIGEST_SIZE);
+  if (mhd_MD5_has_err (&(d_ctx.md5_ctx)))
+    return false;
+#endif /* MHD_SUPPORT_MD5 */
+
+  *expir = (uint_fast32_t) (expiration / 1000u);
+  mhd_PUT_32BIT_LE_UNALIGN (out_buf + mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE, \
+                            (uint32_t) (*expir & UINT32_C (0xFFFFFFFF)));
+
+  return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) bool
+mhd_auth_digest_get_new_nonce (struct MHD_Connection *restrict c,
+                               char out_buf[mhd_AUTH_DIGEST_NONCE_LEN])
+{
+  static const int max_retries = 3;
+  struct MHD_Daemon *restrict d = mhd_daemon_get_master_daemon (c->daemon);
+  uint8_t nonce_bin[mhd_AUTH_DIGEST_NONCE_BIN_SIZE];
+  uint_fast32_t expir;
+  bool nonce_generated;
+  int i;
+
+  mhd_assert (0 != d->auth_dg.cfg.nonces_num);
+  mhd_assert (NULL != d->auth_dg.nonces);
+
+  nonce_generated = false;
+  for (i = 0; i < max_retries; ++i)
+  {
+    bool good_nonce;
+    struct mhd_DaemonAuthDigestNonceData *nonce_slot;
+    if (! gen_new_nonce (d,
+                         c,
+                         nonce_bin,
+                         &expir))
+      continue; /* Failed, re-try */
+
+    nonce_generated = true;
+    nonce_slot = d->auth_dg.nonces
+                 + nonce_to_index (nonce_bin,
+                                   d->auth_dg.cfg.nonces_num);
+    if (! mhd_mutex_lock (&(d->auth_dg.nonces_lock)))
+      return false; /* Failure exit point */
+    /* Check whether the same nonce has been used before */
+    good_nonce = (0 != memcmp (nonce_slot->nonce,
+                               nonce_bin,
+                               sizeof(nonce_slot->nonce)));
+    if (good_nonce)
+    {
+      memcpy (nonce_slot->nonce,
+              nonce_bin,
+              sizeof(nonce_slot->nonce));
+      nonce_slot->valid_time = expir;
+      nonce_slot->max_recvd_nc = 0;
+      nonce_slot->nmask = 0;
+    }
+    else
+    {
+      /* Check whether the same nonce has been used with different expiration
+         time. */
+      nonce_generated = (nonce_slot->valid_time == expir);
+    }
+    mhd_mutex_unlock_chk (&(d->auth_dg.nonces_lock));
+    if (good_nonce)
+      break;
+  }
+  if (! nonce_generated)
+    return false; /* Failure exit point */
+
+  /* Use the generated nonce even if it is duplicated.
+     One of the clients will just get "nonce stale" response with
+     the new nonce. */
+  (void) mhd_bin_to_hex (nonce_bin,
+                         sizeof(nonce_bin),
+                         out_buf);
+  return true; /* Success exit point */
+}
+
+
+/**
+ * Get client's Digest Authorization algorithm type.
+ * If no algorithm is specified by client, MD5 is assumed.
+ * @param params the Digest Authorization 'algorithm' parameter
+ * @return the algorithm type
+ */
+static enum MHD_DigestAuthAlgo
+get_rq_dauth_algo (const struct mhd_RqDAuthParam *const algo_param)
+{
+  if (NULL == algo_param->value.cstr)
+    return MHD_DIGEST_AUTH_ALGO_MD5; /* Assume MD5 by default */
+
+  if (algo_param->quoted)
+  {
+    if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+                                               algo_param->value.len, \
+                                               mhd_MD5_TOKEN))
+      return MHD_DIGEST_AUTH_ALGO_MD5;
+    if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+                                               algo_param->value.len, \
+                                               mhd_SHA256_TOKEN))
+      return MHD_DIGEST_AUTH_ALGO_SHA256;
+    if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+                                               algo_param->value.len, \
+                                               mhd_MD5_TOKEN mhd_SESS_TOKEN))
+      return MHD_DIGEST_AUTH_ALGO_SHA512_256;
+    if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+                                               algo_param->value.len, \
+                                               mhd_SHA512_256_TOKEN \
+                                               mhd_SESS_TOKEN))
+
+      /* Algorithms below are not supported by MHD for authentication */
+
+      return MHD_DIGEST_AUTH_ALGO_MD5_SESSION;
+    if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+                                               algo_param->value.len, \
+                                               mhd_SHA256_TOKEN \
+                                               mhd_SESS_TOKEN))
+      return MHD_DIGEST_AUTH_ALGO_SHA256_SESSION;
+    if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \
+                                               algo_param->value.len, \
+                                               mhd_SHA512_256_TOKEN))
+      return MHD_DIGEST_AUTH_ALGO_SHA512_256_SESSION;
+
+    /* No known algorithm has been detected */
+    return MHD_DIGEST_AUTH_ALGO_INVALID;
+  }
+  /* The algorithm value is not quoted */
+  if (mhd_str_equal_caseless_n_st (mhd_MD5_TOKEN, \
+                                   algo_param->value.cstr, \
+                                   algo_param->value.len))
+    return MHD_DIGEST_AUTH_ALGO_MD5;
+  if (mhd_str_equal_caseless_n_st (mhd_SHA256_TOKEN, \
+                                   algo_param->value.cstr, \
+                                   algo_param->value.len))
+    return MHD_DIGEST_AUTH_ALGO_SHA256;
+  if (mhd_str_equal_caseless_n_st (mhd_SHA512_256_TOKEN, \
+                                   algo_param->value.cstr, \
+                                   algo_param->value.len))
+    return MHD_DIGEST_AUTH_ALGO_SHA512_256;
+
+  /* Algorithms below are not supported by MHD for authentication */
+
+  if (mhd_str_equal_caseless_n_st (mhd_MD5_TOKEN mhd_SESS_TOKEN, \
+                                   algo_param->value.cstr, \
+                                   algo_param->value.len))
+    return MHD_DIGEST_AUTH_ALGO_MD5_SESSION;
+  if (mhd_str_equal_caseless_n_st (mhd_SHA256_TOKEN mhd_SESS_TOKEN, \
+                                   algo_param->value.cstr, \
+                                   algo_param->value.len))
+    return MHD_DIGEST_AUTH_ALGO_SHA256_SESSION;
+  if (mhd_str_equal_caseless_n_st (mhd_SHA512_256_TOKEN mhd_SESS_TOKEN, \
+                                   algo_param->value.cstr, \
+                                   algo_param->value.len))
+    return MHD_DIGEST_AUTH_ALGO_SHA512_256_SESSION;
+
+  /* No known algorithm has been detected */
+  return MHD_DIGEST_AUTH_ALGO_INVALID;
+}
+
+
+/**
+ * Get QOP ('quality of protection') type.
+ * @param qop_param the Digest Authorization 'QOP' parameter
+ * @return detected QOP ('quality of protection') type.
+ */
+static enum MHD_DigestAuthQOP
+get_rq_dauth_qop (const struct mhd_RqDAuthParam *const qop_param)
+{
+  if (NULL == qop_param->value.cstr)
+    return MHD_DIGEST_AUTH_QOP_NONE;
+  if (qop_param->quoted)
+  {
+    if (mhd_str_equal_caseless_quoted_s_bin_n (qop_param->value.cstr, \
+                                               qop_param->value.len, \
+                                               mhd_TOKEN_AUTH))
+      return MHD_DIGEST_AUTH_QOP_AUTH;
+    if (mhd_str_equal_caseless_quoted_s_bin_n (qop_param->value.cstr, \
+                                               qop_param->value.len, \
+                                               mhd_TOKEN_AUTH_INT))
+      return MHD_DIGEST_AUTH_QOP_AUTH_INT;
+  }
+  else
+  {
+    if (mhd_str_equal_caseless_n_st (mhd_TOKEN_AUTH, \
+                                     qop_param->value.cstr, \
+                                     qop_param->value.len))
+      return MHD_DIGEST_AUTH_QOP_AUTH;
+    if (mhd_str_equal_caseless_n_st (mhd_TOKEN_AUTH_INT, \
+                                     qop_param->value.cstr, \
+                                     qop_param->value.len))
+      return MHD_DIGEST_AUTH_QOP_AUTH_INT;
+  }
+  /* No know QOP has been detected */
+  return MHD_DIGEST_AUTH_QOP_INVALID;
+}
+
+
+/**
+ * Parse request Authorization header parameters for Digest Authentication
+ * @param str the header string, everything after "Digest " substring
+ * @param str_len the length of @a str in characters
+ * @param[out] pdauth the pointer to the structure with Digest Authentication
+ *               parameters
+ * @return true if parameters has been successfully parsed,
+ *         false if format of the @a str is invalid
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) bool
+parse_dauth_params (const struct MHD_String *restrict val,
+                    struct mhd_AuthDigesReqParams *restrict pdauth)
+{
+  /* The tokens */
+  static const struct MHD_String nonce_tk = mhd_MSTR_INIT ("nonce");
+  static const struct MHD_String opaque_tk = mhd_MSTR_INIT ("opaque");
+  static const struct MHD_String algorithm_tk = mhd_MSTR_INIT ("algorithm");
+  static const struct MHD_String response_tk = mhd_MSTR_INIT ("response");
+  static const struct MHD_String username_tk = mhd_MSTR_INIT ("username");
+  static const struct MHD_String username_ext_tk = mhd_MSTR_INIT ("username*");
+  static const struct MHD_String realm_tk = mhd_MSTR_INIT ("realm");
+  static const struct MHD_String uri_tk = mhd_MSTR_INIT ("uri");
+  static const struct MHD_String qop_tk = mhd_MSTR_INIT ("qop");
+  static const struct MHD_String cnonce_tk = mhd_MSTR_INIT ("cnonce");
+  static const struct MHD_String nc_tk = mhd_MSTR_INIT ("nc");
+  static const struct MHD_String userhash_tk = mhd_MSTR_INIT ("userhash");
+  /* The locally processed parameters */
+  struct mhd_RqDAuthParam userhash = { {0, NULL}, false};
+  struct mhd_RqDAuthParam algorithm = { {0, NULL}, false};
+  /* Indexes */
+  size_t i;
+  size_t p;
+  /* The list of the tokens.
+     The order of the elements matches the next array. */
+  static const struct MHD_String *const tk_names[] = {
+    &nonce_tk,          /* 0 */
+    &opaque_tk,         /* 1 */
+    &algorithm_tk,      /* 2 */
+    &response_tk,       /* 3 */
+    &username_tk,       /* 4 */
+    &username_ext_tk,   /* 5 */
+    &realm_tk,          /* 6 */
+    &uri_tk,            /* 7 */
+    &qop_tk,            /* 8 */
+    &cnonce_tk,         /* 9 */
+    &nc_tk,             /* 10 */
+    &userhash_tk        /* 11 */
+  };
+  /* The list of the parameters.
+     The order of the elements matches the previous array. */
+  struct mhd_RqDAuthParam *params[sizeof(tk_names) / sizeof(tk_names[0])];
+
+  params[0 ] = &(pdauth->nonce);           /* 0 */
+  params[1 ] = &(pdauth->opaque);          /* 1 */
+  params[2 ] = &algorithm;                 /* 2 */
+  params[3 ] = &(pdauth->response);        /* 3 */
+  params[4 ] = &(pdauth->username);        /* 4 */
+  params[5 ] = &(pdauth->username_ext);    /* 5 */
+  params[6 ] = &(pdauth->realm);           /* 6 */
+  params[7 ] = &(pdauth->uri);             /* 7 */
+  params[8 ] = &(pdauth->qop_raw);         /* 8 */
+  params[9 ] = &(pdauth->cnonce);          /* 9 */
+  params[10] = &(pdauth->nc);              /* 10 */
+  params[11] = &userhash;                  /* 11 */
+
+  mhd_assert (mhd_ARR_NUM_ELEMS (tk_names) == \
+              mhd_ARR_NUM_ELEMS (params));
+  i = 0;
+
+  mhd_assert (' ' != val->cstr[0]);
+  mhd_assert ('\t' != val->cstr[0]);
+
+  while (val->len > i)
+  {
+    size_t left;
+    mhd_assert (' ' != val->cstr[i]);
+    mhd_assert ('\t' != val->cstr[i]);
+
+    left = val->len - i;
+    if ('=' == val->cstr[i])
+      return false; /* The equal sign is not allowed as the first character */
+    for (p = 0; p < mhd_ARR_NUM_ELEMS (tk_names); ++p)
+    {
+      const struct MHD_String *const tk_name = tk_names[p];
+      struct mhd_RqDAuthParam *const param = params[p];
+      if ( (tk_name->len <= left) &&
+           mhd_str_equal_caseless_bin_n (val->cstr + i, tk_name->cstr,
+                                         tk_name->len) &&
+           ((tk_name->len == left) ||
+            ('=' == val->cstr[i + tk_name->len]) ||
+            (' ' == val->cstr[i + tk_name->len]) ||
+            ('\t' == val->cstr[i + tk_name->len]) ||
+            (',' == val->cstr[i + tk_name->len]) ||
+            (';' == val->cstr[i + tk_name->len])) )
+      {
+        size_t value_start;
+        size_t value_len;
+        bool quoted; /* Only mark as "quoted" if backslash-escape used */
+
+        if (tk_name->len == left)
+          return false; /* No equal sign after parameter name, broken data */
+
+        quoted = false;
+        i += tk_name->len;
+        /* Skip all whitespaces before '=' */
+        while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
+          i++;
+        if ((i == val->len) || ('=' != val->cstr[i]))
+          return false; /* No equal sign, broken data */
+        i++;
+        /* Skip all whitespaces after '=' */
+        while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
+          i++;
+        if ((val->len > i) && ('"' == val->cstr[i]))
+        { /* Value is in quotation marks */
+          i++; /* Advance after the opening quote */
+          value_start = i;
+          while (val->len > i && '"' != val->cstr[i])
+          {
+            if ('\\' == val->cstr[i])
+            {
+              i++;
+              quoted = true; /* Have escaped chars */
+            }
+            if (0 == val->cstr[i])
+              return false; /* Binary zero in parameter value */
+            i++;
+          }
+          if (val->len <= i)
+            return false; /* No closing quote */
+          mhd_assert ('"' == val->cstr[i]);
+          value_len = i - value_start;
+          i++; /* Advance after the closing quote */
+        }
+        else
+        {
+          value_start = i;
+          while ((val->len > i) && (',' != val->cstr[i])
+                 && (' ' != val->cstr[i]) && ('\t' != val->cstr[i])
+                 && (';' != val->cstr[i]))
+          {
+            if (0 == val->cstr[i])
+              return false;  /* Binary zero in parameter value */
+            i++;
+          }
+          if (';' == val->cstr[i])
+            return false;  /* Semicolon in parameter value */
+          value_len = i - value_start;
+        }
+        /* Skip all whitespaces after parameter value */
+        while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
+          i++;
+        if ((val->len > i) && (',' != val->cstr[i]))
+          return false; /* Garbage after parameter value */
+
+        /* Have valid parameter name and value */
+        mhd_assert (! quoted || 0 != value_len);
+        param->value.cstr = val->cstr + value_start;
+        param->value.len = value_len;
+        param->quoted = quoted;
+
+        break; /* Found matching parameter name */
+      }
+    }
+    if (p == mhd_ARR_NUM_ELEMS (tk_names))
+    {
+      /* No matching parameter name */
+      while (val->len > i && ',' != val->cstr[i])
+      {
+        if ((0 == val->cstr[i]) || (';' == val->cstr[i]))
+          return false; /* Not allowed characters */
+        if ('"' == val->cstr[i])
+        { /* Skip quoted part */
+          i++; /* Advance after the opening quote */
+          while (val->len > i && '"' != val->cstr[i])
+          {
+            if (0 == val->cstr[i])
+              return false;  /* Binary zero is not allowed */
+            if ('\\' == val->cstr[i])
+              i++;           /* Skip escaped char */
+            i++;
+          }
+          if (val->len <= i)
+            return false; /* No closing quote */
+          mhd_assert ('"' == val->cstr[i]);
+        }
+        i++;
+      }
+    }
+    mhd_assert (val->len == i || ',' == val->cstr[i]);
+    if (val->len > i)
+      i++; /* Advance after ',' */
+    /* Skip all whitespaces before next parameter name */
+    while (i < val->len && (' ' == val->cstr[i] || '\t' == val->cstr[i]))
+      i++;
+  }
+
+  /* Postprocess values */
+
+  if (NULL != userhash.value.cstr)
+  {
+    if (userhash.quoted)
+      pdauth->userhash =
+        mhd_str_equal_caseless_quoted_s_bin_n (userhash.value.cstr, \
+                                               userhash.value.len, \
+                                               "true");
+    else
+      pdauth->userhash =
+        mhd_str_equal_caseless_n_st ("true", userhash.value.cstr, \
+                                     userhash.value.len);
+
+  }
+  else
+    pdauth->userhash = false;
+
+  pdauth->algo = get_rq_dauth_algo (&algorithm);
+  pdauth->qop = get_rq_dauth_qop (&pdauth->qop_raw);
+
+  return true;
+}
+
+
+/**
+ * Find and pre-parse request's Digest Authorisation parameters.
+ *
+ * Function returns result of pre-parsing of the request's "Authorization"
+ * header or returns cached result if the header has been already pre-parsed for
+ * the current request.
+ * @param req the request to process
+ * @return #MHD_SC_OK if succeed,
+ *         #MHD_SC_AUTH_ABSENT if request has no Digest Authorisation,
+ *         #MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA if not enough memory,
+ *         #MHD_SC_REQ_AUTH_DATA_BROKEN if the header is broken.
+ */
+static MHD_FN_PAR_NONNULL_ALL_ enum MHD_StatusCode
+get_rq_auth_digest_params (struct MHD_Request *restrict req)
+{
+  struct MHD_String h_auth_value;
+  struct mhd_AuthDigesReqParams *dauth;
+
+  mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \
+              mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+  mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= \
+              mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+
+  if (NULL != req->auth.digest.rqp)
+    return MHD_SC_OK;
+
+  if (! mhd_request_get_auth_header_value (req,
+                                           mhd_AUTH_HDR_DIGEST,
+                                           &h_auth_value))
+    return MHD_SC_AUTH_ABSENT;
+
+  dauth =
+    (struct mhd_AuthDigesReqParams *)
+    mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, \
+                                            struct MHD_Connection, \
+                                            rq),
+                             sizeof (struct mhd_AuthDigesReqParams));
+
+  if (NULL == dauth)
+    return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA;
+
+  memset (dauth, 0, sizeof(struct mhd_AuthDigesReqParams));
+  if (! parse_dauth_params (&h_auth_value,
+                            dauth))
+    return MHD_SC_REQ_AUTH_DATA_BROKEN;
+
+  req->auth.digest.rqp = dauth;
+
+  return MHD_SC_OK;
+}
+
+
+/**
+ * Get username type used by the client.
+ * This function does not check whether userhash can be decoded or
+ * extended notation (if used) is valid.
+ * @param params the Digest Authorization parameters
+ * @return the type of username
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ enum MHD_DigestAuthUsernameType
+get_rq_uname_type (const struct mhd_AuthDigesReqParams *params)
+{
+  if (NULL != params->username.value.cstr)
+  {
+    if (NULL == params->username_ext.value.cstr)
+      return params->userhash ?
+             MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH :
+             MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD;
+    else  /* Both 'username' and 'username*' are used */
+      return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
+  }
+  else if (NULL != params->username_ext.value.cstr)
+  {
+    if (! params->username_ext.quoted && ! params->userhash &&
+        (mhd_DAUTH_EXT_PARAM_MIN_LEN <= params->username_ext.value.len) )
+      return MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED;
+    else
+      return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
+  }
+
+  return MHD_DIGEST_AUTH_UNAME_TYPE_MISSING;
+}
+
+
+/**
+ * Get total size required for 'username' and 'userhash_bin'
+ * @param params the Digest Authorization parameters
+ * @param uname_type the type of username
+ * @return the total size required for 'username' and
+ *         'userhash_bin' is userhash is used
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ size_t
+get_rq_unames_size (const struct mhd_AuthDigesReqParams *params,
+                    enum MHD_DigestAuthUsernameType uname_type)
+{
+  size_t s;
+
+  mhd_assert (get_rq_uname_type (params) == uname_type);
+  s = 0;
+  if ((MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type) ||
+      (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type) )
+  {
+    s += params->username.value.len + 1; /* Add one byte for zero-termination */
+    if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
+      s += (params->username.value.len + 1) / 2;
+  }
+  else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type)
+    s += params->username_ext.value.len
+         - mhd_DAUTH_EXT_PARAM_MIN_LEN + 1; /* Add one byte for zero-termination */
+  return s;
+}
+
+
+/**
+ * Get unquoted version of Digest Authorization parameter.
+ * This function automatically zero-teminate the result.
+ * @param param the parameter to extract
+ * @param[out] buf the output buffer, must have enough size to hold the result,
+ *                 the recommended size is 'param->value.len + 1'
+ * @return the size of the result, not including the terminating zero
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) size_t
+get_rq_param_unquoted_copy_z (const struct mhd_RqDAuthParam *restrict param,
+                              char *restrict buf)
+{
+  size_t len;
+  mhd_assert (NULL != param->value.cstr);
+  if (! param->quoted)
+  {
+    memcpy (buf, param->value.cstr, param->value.len);
+    buf [param->value.len] = 0;
+    return param->value.len;
+  }
+
+  len = mhd_str_unquote (param->value.cstr, param->value.len, buf);
+  mhd_assert (0 != len);
+  mhd_assert (len < param->value.len);
+  buf[len] = 0;
+  return len;
+}
+
+
+/**
+ * Get decoded version of username from extended notation.
+ * This function automatically zero-teminate the result.
+ * @param uname_ext the string of client's 'username*' parameter value
+ * @param uname_ext_len the length of @a uname_ext in chars
+ * @param[out] buf the output buffer to put decoded username value
+ * @param buf_size the size of @a buf
+ * @return the number of characters copied to the output buffer or
+ *         -1 if wrong extended notation is used.
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (1,2)
+MHD_FN_PAR_OUT_SIZE_ (3,4) ssize_t
+get_rq_extended_uname_copy_z (const char *restrict uname_ext,
+                              size_t uname_ext_len,
+                              char *restrict buf,
+                              size_t buf_size)
+{
+  size_t r;
+  size_t w;
+  if ((size_t) SSIZE_MAX < uname_ext_len)
+    return -1; /* Too long input string */
+
+  if (mhd_DAUTH_EXT_PARAM_MIN_LEN > uname_ext_len)
+    return -1; /* Required prefix is missing */
+
+  if (! mhd_str_equal_caseless_bin_n (
+        uname_ext,
+        mhd_DAUTH_EXT_PARAM_PREFIX,
+        mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX)))
+    return -1; /* Only UTF-8 is supported, as it is implied by RFC 7616 */
+
+  r = mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX);
+  /* Skip language tag */
+  while (r < uname_ext_len && '\'' != uname_ext[r])
+  {
+    const char chr = uname_ext[r];
+    if ((' ' == chr) || ('\t' == chr) || ('\"' == chr) || (',' == chr) ||
+        (';' == chr) )
+      return -1; /* Wrong char in language tag */
+    r++;
+  }
+  if (r >= uname_ext_len)
+    return -1; /* The end of the language tag was not found */
+  r++; /* Advance to the next char */
+
+  w = mhd_str_pct_decode_strict_n (uname_ext + r, uname_ext_len - r,
+                                   buf, buf_size);
+  if ((0 == w) && (0 != uname_ext_len - r))
+    return -1; /* Broken percent encoding */
+  buf[w] = 0; /* Zero terminate the result */
+  mhd_assert (SSIZE_MAX > w);
+  return (ssize_t) w;
+}
+
+
+/**
+ * Get copy of username used by the client.
+ * @param params the Digest Authorization parameters
+ * @param uname_type the type of username
+ * @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
+ */
+static size_t
+get_rq_uname (const struct mhd_AuthDigesReqParams *restrict params,
+              enum MHD_DigestAuthUsernameType uname_type,
+              struct MHD_AuthDigestUsernameInfo *restrict uname_info,
+              uint8_t *restrict buf,
+              size_t buf_size)
+{
+  size_t buf_used;
+
+  buf_used = 0;
+  mhd_assert (get_rq_uname_type (params) == uname_type);
+  mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type);
+  mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type);
+
+  uname_info->username.cstr = NULL;
+  uname_info->username.len = 0;
+  uname_info->userhash_hex.cstr = NULL;
+  uname_info->userhash_hex.len = 0;
+  uname_info->userhash_bin = NULL;
+
+  if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type)
+  {
+    // TODO: Avoid copying string if not quoted
+    uname_info->username.cstr = (char *) (buf + buf_used);
+    uname_info->username.len =
+      get_rq_param_unquoted_copy_z (&params->username,
+                                    (char *) (buf + buf_used));
+    buf_used += uname_info->username.len + 1;
+    uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD;
+  }
+  else if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
+  {
+    size_t res;
+
+    uname_info->userhash_hex.cstr = (char *) (buf + buf_used);
+    uname_info->userhash_hex.len =
+      get_rq_param_unquoted_copy_z (&params->username,
+                                    (char *) (buf + buf_used));
+    buf_used += uname_info->userhash_hex.len + 1;
+    uname_info->userhash_bin = (uint8_t *) (buf + buf_used);
+    res = mhd_hex_to_bin (uname_info->userhash_hex.cstr,
+                          uname_info->userhash_hex.len,
+                          (uint8_t *) (buf + buf_used));
+    if (res != uname_info->userhash_hex.len / 2)
+    {
+      uname_info->userhash_bin = NULL;
+      uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
+    }
+    else
+    {
+      /* Avoid pointers outside allocated region when the size is zero */
+      if (0 == res)
+        uname_info->userhash_bin = (const uint8_t *) uname_info->username.cstr;
+      uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH;
+      buf_used += res;
+    }
+  }
+  else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type)
+  {
+    ssize_t res;
+    res = get_rq_extended_uname_copy_z (params->username_ext.value.cstr,
+                                        params->username_ext.value.len,
+                                        (char *) (buf + buf_used),
+                                        buf_size - buf_used);
+    if (0 > res)
+      uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
+    else
+    {
+      uname_info->username.cstr = (char *) (buf + buf_used);
+      uname_info->username.len = (size_t) res;
+      uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED;
+      buf_used += uname_info->username.len + 1;
+    }
+  }
+  else
+  {
+    mhd_assert (0);
+    uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
+  }
+  mhd_assert (buf_size >= buf_used);
+  return buf_used;
+}
+
+
+/**
+ * Result of request's Digest Authorization 'nc' value extraction
+ */
+enum MHD_FIXED_ENUM_ mhd_GetRqNCResult
+{
+  mhd_GET_RQ_NC_NONE = MHD_DIGEST_AUTH_NC_NONE,    /**< No 'nc' value */
+  mhd_GET_RQ_NC_VALID = MHD_DIGEST_AUTH_NC_NUMBER, /**< Readable 'nc' value */
+  mhd_GET_RQ_NC_TOO_LONG = MHD_DIGEST_AUTH_NC_TOO_LONG, /**< The 'nc' value is too long */
+  mhd_GET_RQ_NC_TOO_LARGE = MHD_DIGEST_AUTH_NC_TOO_LARGE, /**< The 'nc' value is too big to fit uint32_t */
+  mhd_GET_RQ_NC_BROKEN = 0                         /**< The 'nc' value is not a number */
+};
+
+
+/**
+ * Get 'nc' value from request's Authorization header
+ * @param params the request digest authentication
+ * @param[out] nc the pointer to put nc value to
+ * @return enum value indicating the result
+ */
+static enum mhd_GetRqNCResult
+get_rq_nc (const struct mhd_AuthDigesReqParams *params,
+           uint_fast32_t *nc)
+{
+  const struct mhd_RqDAuthParam *const nc_param =
+    &params->nc;
+  char unq[16];
+  const char *val;
+  size_t val_len;
+  size_t res;
+  uint64_t nc_val;
+
+  if (NULL == nc_param->value.cstr)
+    return mhd_GET_RQ_NC_NONE;
+
+  if (0 == nc_param->value.len)
+    return mhd_GET_RQ_NC_BROKEN;
+
+  if (! nc_param->quoted)
+  {
+    val = nc_param->value.cstr;
+    val_len = nc_param->value.len;
+  }
+  else
+  {
+    /* Actually no backslashes must be used in 'nc' */
+    if (sizeof(unq) < params->nc.value.len)
+      return mhd_GET_RQ_NC_TOO_LONG;
+    val_len = mhd_str_unquote (nc_param->value.cstr, nc_param->value.len, unq);
+    if (0 == val_len)
+      return mhd_GET_RQ_NC_BROKEN;
+    val = unq;
+  }
+
+  res = mhd_strx_to_uint64_n (val,
+                              val_len,
+                              &nc_val);
+  if (0 == res)
+  {
+    const char f = val[0];
+    if ( (('9' >= f) && ('0' <= f)) ||
+         (('F' >= f) && ('A' <= f)) ||
+         (('a' <= f) && ('f' >= f)) )
+      return mhd_GET_RQ_NC_TOO_LARGE;
+    else
+      return mhd_GET_RQ_NC_BROKEN;
+  }
+  if (val_len != res)
+    return mhd_GET_RQ_NC_BROKEN;
+  if (nc_val != (nc_val & UINT64_C (0xFFFFFFFF)))
+    return mhd_GET_RQ_NC_TOO_LARGE;
+  *nc = (uint_fast32_t) nc_val;
+  return mhd_GET_RQ_NC_VALID;
+}
+
+
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+enum MHD_StatusCode
+find_and_parse_auth_digest_uname (struct MHD_Request *restrict req)
+{
+  enum MHD_StatusCode res;
+  struct MHD_AuthDigestUsernameInfo *uname_info;
+  enum MHD_DigestAuthUsernameType uname_type;
+  size_t unif_buf_size;
+  uint8_t *unif_buf_ptr;
+  size_t unif_buf_used;
+
+  mhd_assert (NULL == req->auth.digest.info);
+  mhd_assert (NULL == req->auth.digest.uname);
+
+  res = get_rq_auth_digest_params (req);
+  if (MHD_SC_OK != res)
+    return res;
+
+  mhd_assert (NULL != req->auth.digest.rqp);
+
+  uname_type = get_rq_uname_type (req->auth.digest.rqp);
+  if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING == uname_type) ||
+       (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_type) )
+    return MHD_SC_REQ_AUTH_DATA_BROKEN;
+
+  unif_buf_size = get_rq_unames_size (req->auth.digest.rqp, uname_type);
+
+  uname_info =
+    (struct MHD_AuthDigestUsernameInfo *)
+    mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, struct MHD_Connection, rq),
+                             (sizeof(struct MHD_AuthDigestUsernameInfo))
+                             + unif_buf_size);
+  if (NULL == uname_info)
+    return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA;
+  memset (uname_info,
+          0,
+          (sizeof(struct MHD_AuthDigestUsernameInfo)) + unif_buf_size);
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+  uname_info->username.cstr = NULL;
+  uname_info->userhash_hex.cstr = NULL;
+  uname_info->userhash_bin = NULL;
+#endif
+
+  uname_info->algo = req->auth.digest.rqp->algo;
+  unif_buf_ptr = (uint8_t *) (uname_info + 1);
+  unif_buf_used = get_rq_uname (req->auth.digest.rqp,
+                                uname_type, uname_info,
+                                unif_buf_ptr,
+                                unif_buf_size);
+  mhd_assert (unif_buf_size >= unif_buf_used);
+  (void) unif_buf_used; /* Mute compiler warning on non-debug builds */
+  mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_info->uname_type);
+
+  req->auth.digest.uname = uname_info;
+  if (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_info->uname_type)
+    return MHD_SC_REQ_AUTH_DATA_BROKEN;
+  mhd_assert (uname_type == uname_info->uname_type);
+
+  return MHD_SC_OK;
+}
+
+
+MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) enum MHD_StatusCode
+mhd_request_get_auth_digest_username (
+  struct MHD_Request *restrict req,
+  const struct MHD_AuthDigestUsernameInfo **restrict v_auth_digest_uname)
+{
+  mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \
+              mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+  mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= \
+              mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+
+  if (MHD_SC_OK != req->auth.digest.parse_result)
+    return req->auth.digest.parse_result;
+
+  if (NULL == req->auth.digest.uname)
+    req->auth.digest.parse_result = find_and_parse_auth_digest_uname (req);
+
+  if (MHD_SC_OK != req->auth.digest.parse_result)
+    return req->auth.digest.parse_result; /* Failure exit point */
+
+  mhd_assert (NULL != req->auth.digest.uname);
+  *v_auth_digest_uname = req->auth.digest.uname;
+
+  return MHD_SC_OK; /* Success exit point */
+}
+
+
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+enum MHD_StatusCode
+find_and_parse_auth_digest_info (struct MHD_Request *restrict req)
+{
+  enum MHD_StatusCode res;
+  struct MHD_AuthDigestInfo *info;
+  enum MHD_DigestAuthUsernameType uname_type;
+  size_t unif_buf_size;
+  uint8_t *unif_buf_ptr;
+  size_t unif_buf_used;
+  enum mhd_GetRqNCResult nc_res;
+
+  mhd_assert (NULL == req->auth.digest.info);
+
+  res = get_rq_auth_digest_params (req);
+  if (MHD_SC_OK != res)
+    return res;
+
+  unif_buf_size = 0;
+
+  uname_type = get_rq_uname_type (req->auth.digest.rqp);
+
+  unif_buf_size += get_rq_unames_size (req->auth.digest.rqp,
+                                       uname_type);
+
+  if (NULL != req->auth.digest.rqp->opaque.value.cstr)
+    unif_buf_size += req->auth.digest.rqp->opaque.value.len + 1;  /* Add one for zero-termination */
+  if (NULL != req->auth.digest.rqp->realm.value.cstr)
+    unif_buf_size += req->auth.digest.rqp->realm.value.len + 1;   /* Add one for zero-termination */
+  info =
+    (struct MHD_AuthDigestInfo *)
+    mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, struct MHD_Connection, rq),
+                             (sizeof(struct MHD_AuthDigestInfo))
+                             + unif_buf_size);
+  if (NULL == info)
+    return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA;
+
+  memset (info,
+          0,
+          (sizeof(struct MHD_AuthDigestInfo)) + unif_buf_size);
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+  info->username.cstr = NULL;
+  info->userhash_hex.cstr = NULL;
+  info->userhash_bin = NULL;
+  info->opaque.cstr = NULL;
+  info->realm.cstr = NULL;
+#endif
+
+  unif_buf_ptr = (uint8_t *) (info + 1);
+  unif_buf_used = 0;
+
+  info->algo = req->auth.digest.rqp->algo;
+
+  if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type) &&
+       (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type) )
+    unif_buf_used +=
+      get_rq_uname (req->auth.digest.rqp, uname_type,
+                    (struct MHD_AuthDigestUsernameInfo *) info,
+                    unif_buf_ptr + unif_buf_used,
+                    unif_buf_size - unif_buf_used);
+  else
+    info->uname_type = uname_type;
+
+  if (NULL != req->auth.digest.rqp->opaque.value.cstr)
+  {
+    info->opaque.cstr = (char *) (unif_buf_ptr + unif_buf_used);
+    info->opaque.len =
+      get_rq_param_unquoted_copy_z (&(req->auth.digest.rqp->opaque),
+                                    (char *) (unif_buf_ptr + unif_buf_used));
+    unif_buf_used += info->opaque.len + 1;
+  }
+  if (NULL != req->auth.digest.rqp->realm.value.cstr)
+  {
+    info->realm.cstr = (char *) (unif_buf_ptr + unif_buf_used);
+    info->realm.len =
+      get_rq_param_unquoted_copy_z (&(req->auth.digest.rqp->realm),
+                                    (char *) (unif_buf_ptr + unif_buf_used));
+    unif_buf_used += info->realm.len + 1;
+  }
+
+  mhd_assert (unif_buf_size >= unif_buf_used);
+
+  info->qop = req->auth.digest.rqp->qop;
+
+  if (NULL != req->auth.digest.rqp->cnonce.value.cstr)
+    info->cnonce_len = req->auth.digest.rqp->cnonce.value.len;
+  else
+    info->cnonce_len = 0;
+
+  nc_res = get_rq_nc (req->auth.digest.rqp, &info->nc);
+  if (mhd_GET_RQ_NC_VALID == nc_res)
+  {
+    if (0 == info->nc)
+      info->nc_type = MHD_DIGEST_AUTH_NC_ZERO;
+    else
+      info->nc_type = MHD_DIGEST_AUTH_NC_NUMBER;
+  }
+  else
+  {
+    info->nc = 0;
+    if (mhd_GET_RQ_NC_BROKEN == nc_res)
+      info->nc_type = MHD_DIGEST_AUTH_NC_NONE;
+    else
+      info->nc_type = (enum MHD_DigestAuthNC) nc_res;
+  }
+
+  req->auth.digest.info = info;
+  if (NULL == req->auth.digest.uname)
+    req->auth.digest.uname = (struct MHD_AuthDigestUsernameInfo *) info;
+
+  mhd_assert (uname_type == info->uname_type);
+  mhd_assert (uname_type == req->auth.digest.uname->uname_type);
+
+  if ((MHD_DIGEST_AUTH_UNAME_TYPE_MISSING == uname_type) ||
+      (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_type) ||
+      ((mhd_GET_RQ_NC_BROKEN == nc_res)))
+    return MHD_SC_REQ_AUTH_DATA_BROKEN;
+
+  return MHD_SC_OK;
+}
+
+
+MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) enum MHD_StatusCode
+mhd_request_get_auth_digest_info (
+  struct MHD_Request *restrict req,
+  const struct MHD_AuthDigestInfo **restrict v_auth_digest_info)
+{
+  mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \
+              mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+  mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= \
+              mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage);
+
+  if (MHD_SC_OK != req->auth.digest.parse_result)
+    return req->auth.digest.parse_result;
+
+  if (NULL == req->auth.digest.info)
+    req->auth.digest.parse_result = find_and_parse_auth_digest_info (req);
+
+  if (MHD_SC_OK != req->auth.digest.parse_result)
+    return req->auth.digest.parse_result; /* Failure exit point */
+
+  mhd_assert (NULL != req->auth.digest.info);
+  mhd_assert (NULL != req->auth.digest.uname);
+  *v_auth_digest_info = req->auth.digest.info;
+
+  return MHD_SC_OK; /* Success exit point */
+}
+
+
+/**
+ * Get base hash calculation algorithm from #MHD_DigestAuthAlgo value.
+ * @param algo the MHD_DigestAuthAlgo value
+ * @return the base hash calculation algorithm
+ */
+MHD_static_inline_ enum MHD_DigestBaseAlgo
+get_base_digest_algo (enum MHD_DigestAuthAlgo algo)
+{
+  unsigned int base_algo;
+
+  base_algo =
+    ((unsigned int) algo)
+    & ~((unsigned int)
+        (MHD_DIGEST_AUTH_ALGO_NON_SESSION
+         | MHD_DIGEST_AUTH_ALGO_SESSION));
+  return (enum MHD_DigestBaseAlgo) base_algo;
+}
+
+
+/**
+ * Get digest size in bytes for specified algorithm.
+ *
+ * Internal inline version.
+ * @param algo the algorithm to check
+ * @return the size of the digest (in bytes) or zero if the input value is not
+ *         supported/valid
+ */
+MHD_static_inline_ size_t
+digest_get_hash_size (enum MHD_DigestAuthAlgo algo)
+{
+#ifdef MHD_SUPPORT_MD5
+  mhd_assert (MHD_MD5_DIGEST_SIZE == mhd_MD5_DIGEST_SIZE);
+#endif /* MHD_SUPPORT_MD5 */
+#ifdef MHD_SUPPORT_SHA256
+  mhd_assert (MHD_SHA256_DIGEST_SIZE == mhd_SHA256_DIGEST_SIZE);
+#endif /* MHD_SUPPORT_SHA256 */
+#ifdef MHD_SUPPORT_SHA512_256
+  mhd_assert (MHD_SHA512_256_DIGEST_SIZE == mhd_SHA512_256_DIGEST_SIZE);
+#ifdef MHD_SUPPORT_SHA256
+  mhd_assert (mhd_SHA256_DIGEST_SIZE == mhd_SHA512_256_DIGEST_SIZE);
+#endif /* MHD_SUPPORT_SHA256 */
+#endif /* MHD_SUPPORT_SHA512_256 */
+  /* Only one algorithm must be specified */
+  mhd_assert (1 == \
+              (((0 != (algo & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0)   \
+               + ((0 != (algo & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0)   \
+               + ((0 != (algo & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0)));
+#ifdef MHD_SUPPORT_MD5
+  if (0 != (((unsigned int) algo)
+            & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5)))
+    return MHD_MD5_DIGEST_SIZE;
+  else
+#endif /* MHD_SUPPORT_MD5 */
+#if defined(MHD_SUPPORT_SHA256) && defined(MHD_SUPPORT_SHA512_256)
+  if (0 != (((unsigned int) algo)
+            & ( ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256)
+                | ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA512_256))))
+    return MHD_SHA256_DIGEST_SIZE; /* The same as mhd_SHA512_256_DIGEST_SIZE */
+  else
+#elif defined(MHD_SUPPORT_SHA256)
+  if (0 != (((unsigned int) algo)
+            & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256)))
+    return MHD_SHA256_DIGEST_SIZE;
+  else
+#elif defined(MHD_SUPPORT_SHA512_256)
+  if (0 != (((unsigned int) algo)
+            & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA512_256)))
+    return MHD_SHA512_256_DIGEST_SIZE;
+  else
+#endif /* MHD_SUPPORT_SHA512_256 */
+    (void) 0; /* Unsupported algorithm */
+
+  return 0; /* Wrong input or unsupported algorithm */
+}
+
+
+/**
+ * Get digest size for specified algorithm.
+ *
+ * The size of the digest specifies the size of the userhash, userdigest
+ * and other parameters which size depends on used hash algorithm.
+ * @param algo the algorithm to check
+ * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
+ *         #MHD_SHA256_DIGEST_SIZE/MHD_SHA512_256_DIGEST_SIZE)
+ *         or zero if the input value is not supported or not valid
+ * @sa #MHD_digest_auth_calc_userdigest()
+ * @sa #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_calc_userhash_hex()
+ * @note Available since #MHD_VERSION 0x00097701
+ * @ingroup authentication
+ */
+MHD_EXTERN_ MHD_FN_CONST_ size_t
+MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo algo)
+{
+  return digest_get_hash_size (algo);
+}
+
+
+/**
+ * The digest calculation structure.
+ */
+struct DigestAlgorithm
+{
+  /**
+   * A context for the digest algorithm, already initialized to be
+   * useful for @e init, @e update and @e digest.
+   */
+  union DigestCtx ctx;
+
+  /**
+   * The hash calculation algorithm.
+   */
+  enum MHD_DigestBaseAlgo algo;
+
+  /**
+   * Buffer for hex-print of the final digest.
+   */
+#ifdef _DEBUG
+  bool uninitialised; /**< The structure has been not set-up */
+  bool algo_selected; /**< The algorithm has been selected */
+  bool ready_for_hashing; /**< The structure is ready to hash data */
+  bool hashing; /**< Some data has been hashed, but the digest has not finalised yet */
+#endif /* _DEBUG */
+};
+
+
+/**
+ * Return the size of the digest.
+ * @param da the digest calculation structure to identify
+ * @return the size of the digest.
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ unsigned int
+digest_get_size (struct DigestAlgorithm *da)
+{
+  mhd_assert (! da->uninitialised);
+  mhd_assert (da->algo_selected);
+#ifdef MHD_SUPPORT_MD5
+  if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
+    return mhd_MD5_DIGEST_SIZE;
+#endif /* MHD_SUPPORT_MD5 */
+#ifdef MHD_SUPPORT_SHA256
+  if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
+    return mhd_SHA256_DIGEST_SIZE;
+#endif /* MHD_SUPPORT_SHA256 */
+#ifdef MHD_SUPPORT_SHA512_256
+  if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
+    return mhd_SHA512_256_DIGEST_SIZE;
+#endif /* MHD_SUPPORT_SHA512_256 */
+  mhd_UNREACHABLE ();
+  return 0;
+}
+
+
+#if defined(mhd_MD5_HAS_DEINIT) \
+  || defined(mhd_SHA256_HAS_DEINIT) \
+  || defined(mhd_SHA512_256_HAS_DEINIT)
+/**
+ * Indicates presence of digest_deinit() function
+ */
+#define mhd_DIGEST_HAS_DEINIT 1
+#endif /* mhd_MD5_HAS_DEINIT || mhd_SHA256_HAS_DEINIT */
+
+#ifdef mhd_DIGEST_HAS_DEINIT
+/**
+ * Zero-initialise digest calculation structure.
+ *
+ * This initialisation is enough to safely call #digest_deinit() only.
+ * To make any real digest calculation, #digest_setup_and_init() must be called.
+ * @param da the digest calculation
+ */
+MHD_static_inline_ void
+digest_setup_zero (struct DigestAlgorithm *da)
+{
+#ifdef _DEBUG
+  da->uninitialised = false;
+  da->algo_selected = false;
+  da->ready_for_hashing = false;
+  da->hashing = false;
+#endif /* _DEBUG */
+  da->algo = MHD_DIGEST_BASE_ALGO_INVALID;
+}
+
+
+/**
+ * De-initialise digest calculation structure.
+ *
+ * This function must be called if #digest_setup_and_init() was called for
+ * @a da.
+ * This function must not be called if @a da was not initialised by
+ * #digest_setup_and_init() or by #digest_setup_zero().
+ * @param da the digest calculation
+ */
+MHD_static_inline_ void
+digest_deinit (struct DigestAlgorithm *da)
+{
+  mhd_assert (! da->uninitialised);
+#ifdef mhd_MD5_HAS_DEINIT
+  if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
+    mhd_MD5_deinit (&(da->ctx.md5_ctx));
+  else
+#endif /* mhd_MD5_HAS_DEINIT */
+#ifdef mhd_SHA256_HAS_DEINIT
+  if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
+    mhd_SHA256_deinit (&(da->ctx.sha256_ctx));
+  else
+#endif /* mhd_SHA256_HAS_DEINIT */
+#ifdef mhd_SHA512_256_HAS_DEINIT
+  if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
+    mhd_SHA512_256_deinit (&(da->ctx.sha256_ctx));
+  else
+#endif /* mhd_SHA512_256_HAS_DEINIT */
+  (void) 0;
+  digest_setup_zero (da);
+}
+
+
+#else  /* ! mhd_DIGEST_HAS_DEINIT */
+#define digest_setup_zero(da) ((void) 0)
+#define digest_deinit(da) ((void) 0)
+#endif /* ! mhd_DIGEST_HAS_DEINIT */
+
+
+/**
+ * Set-up the digest calculation structure and initialise with initial values.
+ *
+ * If @a da was successfully initialised, #digest_deinit() must be called
+ * after finishing using of the @a da.
+ *
+ * This function must not be called more than once for any @a da.
+ *
+ * @param da the structure to set-up
+ * @param algo the algorithm to use for digest calculation
+ * @return boolean 'true' if successfully set-up,
+ *         false otherwise.
+ */
+MHD_static_inline_ MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ bool
+digest_init_one_time (struct DigestAlgorithm *da,
+                      enum MHD_DigestBaseAlgo algo)
+{
+#ifdef _DEBUG
+  da->uninitialised = false;
+  da->algo_selected = false;
+  da->ready_for_hashing = false;
+  da->hashing = false;
+#endif /* _DEBUG */
+  switch (algo)
+  {
+  case MHD_DIGEST_BASE_ALGO_MD5:
+#ifdef MHD_SUPPORT_MD5
+    da->algo = MHD_DIGEST_BASE_ALGO_MD5;
+#  ifdef _DEBUG
+    da->algo_selected = true;
+#  endif
+    mhd_MD5_init_one_time (&(da->ctx.md5_ctx));
+#  ifdef _DEBUG
+    da->ready_for_hashing = true;
+#  endif
+    return true;
+#endif /* MHD_SUPPORT_MD5 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_SHA256:
+#ifdef MHD_SUPPORT_SHA256
+    da->algo = MHD_DIGEST_BASE_ALGO_SHA256;
+#  ifdef _DEBUG
+    da->algo_selected = true;
+#  endif
+    mhd_SHA256_init_one_time (&(da->ctx.sha256_ctx));
+#  ifdef _DEBUG
+    da->ready_for_hashing = true;
+#  endif
+    return true;
+#endif /* MHD_SUPPORT_SHA256 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_SHA512_256:
+#ifdef MHD_SUPPORT_SHA512_256
+    da->algo = MHD_DIGEST_BASE_ALGO_SHA512_256;
+#  ifdef _DEBUG
+    da->algo_selected = true;
+#  endif
+    mhd_SHA512_256_init_one_time (&(da->ctx.sha512_256_ctx));
+#  ifdef _DEBUG
+    da->ready_for_hashing = true;
+#  endif
+    return true;
+#endif /* MHD_SUPPORT_SHA512_256 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_INVALID:
+  default:
+    mhd_UNREACHABLE ();
+    break;
+  }
+  da->algo = MHD_DIGEST_BASE_ALGO_INVALID;
+  return false; /* Unsupported or bad algorithm */
+}
+
+
+/**
+ * Hash more data for digest calculation.
+ * @param da the digest calculation
+ * @param size the size of the @a data in bytes
+ * @param data the data to process
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) void
+digest_update (struct DigestAlgorithm *restrict da,
+               size_t size,
+               const void *restrict data)
+{
+  mhd_assert (! da->uninitialised);
+  mhd_assert (da->algo_selected);
+  mhd_assert (da->ready_for_hashing);
+  switch (da->algo)
+  {
+  case MHD_DIGEST_BASE_ALGO_MD5:
+#ifdef MHD_SUPPORT_MD5
+    mhd_MD5_update (&da->ctx.md5_ctx,
+                    size,
+                    (const uint8_t *) data);
+#else
+    mhd_UNREACHABLE ();
+#endif
+    break;
+  case MHD_DIGEST_BASE_ALGO_SHA256:
+#ifdef MHD_SUPPORT_SHA256
+    mhd_SHA256_update (&da->ctx.sha256_ctx,
+                       size,
+                       (const uint8_t *) data);
+#else
+    mhd_UNREACHABLE ();
+#endif
+    break;
+  case MHD_DIGEST_BASE_ALGO_SHA512_256:
+#ifdef MHD_SUPPORT_SHA512_256
+    mhd_SHA512_256_update (&da->ctx.sha512_256_ctx,
+                           size,
+                           (const uint8_t *) data);
+#else
+    mhd_UNREACHABLE ();
+#endif
+    break;
+  case MHD_DIGEST_BASE_ALGO_INVALID:
+  default:
+    mhd_UNREACHABLE ();
+    break;
+  }
+#ifdef _DEBUG
+  da->hashing = true;
+#endif
+}
+
+
+/**
+ * Feed digest calculation with more data from string.
+ * @param da the digest calculation
+ * @param str the zero-terminated string to process
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) void
+digest_update_str (struct DigestAlgorithm *restrict da,
+                   const char *restrict str)
+{
+  digest_update (da,
+                 strlen (str),
+                 (const uint8_t *) str);
+}
+
+
+/**
+ * Feed digest calculation with more data from string.
+ * @param da the digest calculation
+ * @param buf the sized buffer with the data
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) void
+digest_update_cbuf (struct DigestAlgorithm *restrict da,
+                    const struct mhd_BufferConst *restrict buf)
+{
+  digest_update (da,
+                 buf->size,
+                 (const uint8_t *) buf->data);
+}
+
+
+/**
+ * Feed digest calculation with more data from string.
+ * @param da the digest calculation
+ * @param buf the sized buffer with the data
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) void
+digest_update_buf (struct DigestAlgorithm *restrict da,
+                   const struct mhd_Buffer *restrict buf)
+{
+  digest_update (da,
+                 buf->size,
+                 (const uint8_t *) buf->data);
+}
+
+
+/**
+ * Feed digest calculation with single colon ':' character.
+ * @param da the digest calculation
+ * @param str the zero-terminated string to process
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ void
+digest_update_with_colon (struct DigestAlgorithm *da)
+{
+  static const uint8_t colon = (uint8_t) ':';
+  digest_update (da,
+                 1,
+                 &colon);
+}
+
+
+/**
+ * Finally calculate hash (the digest).
+ * @param da the digest calculation
+ * @param[out] digest the pointer to the buffer to put calculated digest,
+ *                    must be at least digest_get_size(da) bytes large
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) void
+digest_calc_hash (struct DigestAlgorithm *da,
+                  uint8_t *digest)
+{
+  mhd_assert (! da->uninitialised);
+  mhd_assert (da->algo_selected);
+  mhd_assert (da->ready_for_hashing);
+  switch (da->algo)
+  {
+  case MHD_DIGEST_BASE_ALGO_MD5:
+#ifdef MHD_SUPPORT_MD5
+#  ifdef mhd_MD5_HAS_FINISH
+    mhd_MD5_finish (&da->ctx.md5_ctx, digest);
+#    ifdef _DEBUG
+    da->ready_for_hashing = false;
+#    endif /* _DEBUG */
+#  else  /* ! mhd_MD5_HAS_FINISH */
+    mhd_MD5_finish_reset (&da->ctx.md5_ctx, digest);
+#    ifdef _DEBUG
+    da->ready_for_hashing = true;
+#    endif /* _DEBUG */
+#  endif /* ! mhd_MD5_HAS_FINISH */
+#else  /* ! MHD_SUPPORT_MD5 */
+    mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_MD5 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_SHA256:
+#ifdef MHD_SUPPORT_SHA256
+#  ifdef mhd_SHA256_HAS_FINISH
+    mhd_SHA256_finish (&da->ctx.sha256_ctx, digest);
+#    ifdef _DEBUG
+    da->ready_for_hashing = false;
+#    endif /* _DEBUG */
+#  else  /* ! mhd_SHA256_HAS_FINISH */
+    mhd_SHA256_finish_reset (&da->ctx.sha256_ctx, digest);
+#    ifdef _DEBUG
+    da->ready_for_hashing = true;
+#    endif /* _DEBUG */
+#  endif /* ! mhd_SHA256_HAS_FINISH */
+#else  /* ! MHD_SUPPORT_SHA256 */
+    mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA256 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_SHA512_256:
+#ifdef MHD_SUPPORT_SHA512_256
+#ifdef mhd_SHA512_256_HAS_FINISH
+    mhd_SHA512_256_finish (&da->ctx.sha512_256_ctx, digest);
+#ifdef _DEBUG
+    da->ready_for_hashing = false;
+#endif /* _DEBUG */
+#else  /* ! mhd_SHA512_256_HAS_FINISH */
+    mhd_SHA512_256_finish_reset (&da->ctx.sha512_256_ctx, digest);
+#ifdef _DEBUG
+    da->ready_for_hashing = true;
+#endif /* _DEBUG */
+#endif /* ! mhd_SHA512_256_HAS_FINISH */
+#else  /* ! MHD_SUPPORT_SHA512_256 */
+    mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA512_256 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_INVALID:
+  default:
+    mhd_UNREACHABLE ();
+    break;
+  }
+#ifdef _DEBUG
+  da->hashing = false;
+#endif /* _DEBUG */
+}
+
+
+/**
+ * Reset the digest calculation structure and prepare for new calculation.
+ *
+ * @param da the structure to reset
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ void
+digest_reset (struct DigestAlgorithm *da)
+{
+  mhd_assert (! da->uninitialised);
+  mhd_assert (da->algo_selected);
+  mhd_assert (! da->hashing);
+  switch (da->algo)
+  {
+  case MHD_DIGEST_BASE_ALGO_MD5:
+#ifdef MHD_SUPPORT_MD5
+#  ifdef mhd_MD5_HAS_FINISH
+    mhd_assert (! da->ready_for_hashing);
+#  else  /* ! mhd_MD5_HAS_FINISH */
+    mhd_assert (da->ready_for_hashing);
+#  endif /* ! mhd_MD5_HAS_FINISH */
+    mhd_MD5_reset (&(da->ctx.md5_ctx));
+#  ifdef _DEBUG
+    da->ready_for_hashing = true;
+#  endif /* _DEBUG */
+#else  /* ! MHD_SUPPORT_MD5 */
+    mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_MD5 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_SHA256:
+#ifdef MHD_SUPPORT_SHA256
+#ifdef mhd_SHA256_HAS_FINISH
+    mhd_assert (! da->ready_for_hashing);
+#else  /* ! mhd_SHA256_HAS_FINISH */
+    mhd_assert (da->ready_for_hashing);
+#endif /* ! mhd_SHA256_HAS_FINISH */
+    mhd_SHA256_reset (&(da->ctx.sha256_ctx));
+#ifdef _DEBUG
+    da->ready_for_hashing = true;
+#endif /* _DEBUG */
+#else  /* ! MHD_SUPPORT_SHA256 */
+    mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA256 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_SHA512_256:
+#ifdef MHD_SUPPORT_SHA512_256
+#  ifdef mhd_SHA512_256_HAS_FINISH
+    mhd_assert (! da->ready_for_hashing);
+#  else  /* ! mhd_SHA512_256_HAS_FINISH */
+    mhd_assert (da->ready_for_hashing);
+#  endif /* ! mhd_SHA512_256_HAS_FINISH */
+    mhd_SHA512_256_reset (&(da->ctx.sha512_256_ctx));
+#  ifdef _DEBUG
+    da->ready_for_hashing = true;
+#  endif /* _DEBUG */
+#else  /* ! MHD_SUPPORT_SHA512_256 */
+    mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA512_256 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_INVALID:
+  default:
+    da->ready_for_hashing = false;
+    mhd_UNREACHABLE ();
+    break;
+  }
+}
+
+
+#if defined(mhd_MD5_HAS_EXT_ERROR) \
+  || defined(mhd_SHA256_HAS_EXT_ERROR) \
+  || defined(mhd_SHA512_256_HAS_EXT_ERROR)
+/**
+ * Indicates that digest algorithm has external error status
+ */
+#define mhd_DIGEST_HAS_EXT_ERROR 1
+#endif /* mhd_MD5_HAS_EXT_ERROR || mhd_SHA256_HAS_EXT_ERROR
+          || mhd_SHA512_256_HAS_EXT_ERROR*/
+
+#ifdef mhd_DIGEST_HAS_EXT_ERROR
+/**
+ * Get external error state.
+ *
+ * When external digest calculation used, an error may occur during
+ * initialisation or hashing data. This function checks whether external
+ * error has been reported for digest calculation.
+ * @param da the digest calculation
+ * @return 'true' if external error occurs,
+ *         'false' otherwise
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ bool
+digest_has_error (struct DigestAlgorithm *da)
+{
+  mhd_assert (! da->uninitialised);
+  mhd_assert (da->algo_selected);
+  switch (da->algo)
+  {
+  case MHD_DIGEST_BASE_ALGO_MD5:
+#ifdef MHD_SUPPORT_MD5
+    return mhd_MD5_has_err (&(da->ctx.md5_ctx));
+#else  /* ! MHD_SUPPORT_MD5 */
+    mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_MD5 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_SHA256:
+#ifdef MHD_SUPPORT_SHA256
+    return mhd_SHA256_has_err (&(da->ctx.sha256_ctx));
+#else  /* ! MHD_SUPPORT_SHA256 */
+    mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA256 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_SHA512_256:
+#ifdef MHD_SUPPORT_SHA512_256
+    return mhd_SHA512_256_has_err (&(da->ctx.sha512_256_ctx));
+#else  /* ! MHD_SUPPORT_SHA512_256 */
+    mhd_UNREACHABLE ();
+#endif /* ! MHD_SUPPORT_SHA512_256 */
+    break;
+
+  case MHD_DIGEST_BASE_ALGO_INVALID:
+  default:
+    break;
+  }
+  mhd_UNREACHABLE ();
+  return true;
+}
+
+
+#else  /* ! mhd_DIGEST_HAS_EXT_ERROR */
+#define digest_has_error(da) (((void) (da)), ! ! 0)
+#endif /* ! mhd_DIGEST_HAS_EXT_ERROR */
+
+
+/**
+ * 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_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (2, 3) MHD_FN_PAR_IN_SIZE_ (4, 5)
+MHD_FN_PAR_CSTR_ (6) MHD_FN_PAR_OUT_ (7) void
+calc_userdigest (struct DigestAlgorithm *restrict da,
+                 const char *restrict username, const size_t username_len,
+                 const char *restrict realm, const size_t realm_len,
+                 const char *restrict password,
+                 uint8_t *ha1_bin)
+{
+  mhd_assert (! da->uninitialised);
+  mhd_assert (da->algo_selected);
+  mhd_assert (! da->hashing);
+  digest_update (da, username_len, username);
+  digest_update_with_colon (da);
+  digest_update (da, realm_len, realm);
+  digest_update_with_colon (da);
+  digest_update_str (da, password);
+  digest_calc_hash (da, ha1_bin);
+}
+
+
+MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4)
+MHD_FN_PAR_OUT_SIZE_ (6,5) enum MHD_StatusCode
+MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo algo,
+                                 const char *MHD_RESTRICT username,
+                                 const char *MHD_RESTRICT realm,
+                                 const char *MHD_RESTRICT password,
+                                 size_t bin_buf_size,
+                                 void *MHD_RESTRICT userdigest_bin)
+{
+  struct DigestAlgorithm da;
+  enum MHD_StatusCode ret;
+  if (! digest_init_one_time (&da, get_base_digest_algo (algo)))
+    return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED;
+
+  if (digest_get_size (&da) > bin_buf_size)
+    ret = MHD_SC_OUT_BUFF_TOO_SMALL;
+  else
+  {
+    calc_userdigest (&da,
+                     username,
+                     strlen (username),
+                     realm,
+                     strlen (realm),
+                     password,
+                     userdigest_bin);
+    ret = digest_has_error (&da) ? MHD_SC_HASH_FAILED : MHD_SC_OK;
+  }
+  digest_deinit (&da);
+
+  return ret;
+}
+
+
+/**
+ * Calculate userhash, return it as binary data.
+ *
+ * MHD internal version.
+ *
+ * @param da the digest algorithm
+ * @param username_len the length of the @a username
+ * @param username the username to use
+ * @param realm_len the length of the @a realm
+ * @param realm the realm to use
+ * @param[out] digest_bin the output buffer, must have at least
+ *                        #MHD_digest_get_hash_size(algo) bytes available
+ */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) MHD_FN_PAR_OUT_ (6) void
+calc_userhash (struct DigestAlgorithm *da,
+               const size_t username_len,
+               const char *username,
+               const size_t realm_len,
+               const char *realm,
+               uint8_t *digest_bin)
+{
+  mhd_assert (! da->uninitialised);
+  mhd_assert (da->algo_selected);
+  mhd_assert (! da->hashing);
+  digest_update (da, username_len, username);
+  digest_update_with_colon (da);
+  digest_update (da, realm_len, realm);
+  digest_calc_hash (da, digest_bin);
+}
+
+
+MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4) enum MHD_StatusCode
+MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo algo,
+                               const char *MHD_RESTRICT username,
+                               const char *MHD_RESTRICT realm,
+                               size_t bin_buf_size,
+                               void *MHD_RESTRICT userhash_bin)
+{
+  struct DigestAlgorithm da;
+  enum MHD_StatusCode ret;
+
+  if (! digest_init_one_time (&da, get_base_digest_algo (algo)))
+    return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED;
+  if (digest_get_size (&da) > bin_buf_size)
+    ret = MHD_SC_OUT_BUFF_TOO_SMALL;
+  else
+  {
+    calc_userhash (&da,
+                   strlen (username),
+                   username,
+                   strlen (realm),
+                   realm,
+                   userhash_bin);
+
+    ret = digest_has_error (&da) ? MHD_SC_HASH_FAILED : MHD_SC_OK;
+  }
+  digest_deinit (&da);
+
+  return ret;
+}
+
+
+MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5,4) enum MHD_StatusCode
+MHD_digest_auth_calc_userhash_hex (
+  enum MHD_DigestAuthAlgo algo,
+  const char *MHD_RESTRICT username,
+  const char *MHD_RESTRICT realm,
+  size_t hex_buf_size,
+  char userhash_hex[MHD_FN_PAR_DYN_ARR_SIZE_ (hex_buf_size)])
+{
+  uint8_t userhash_bin[mhd_MAX_DIGEST];
+  size_t digest_size;
+  enum MHD_StatusCode res;
+
+  digest_size = digest_get_hash_size (algo);
+  if (digest_size * 2 + 1 > hex_buf_size)
+    return MHD_SC_OUT_BUFF_TOO_SMALL;
+  res = MHD_digest_auth_calc_userhash (algo,
+                                       username,
+                                       realm,
+                                       sizeof(userhash_bin),
+                                       userhash_bin);
+  if (MHD_SC_OK != res)
+    return res;
+
+  (void) mhd_bin_to_hex_z (userhash_bin,
+                           digest_size,
+                           userhash_hex);
+  return MHD_SC_OK;
+}
+
+
+/**
+ * Extract timestamp from the given nonce.
+ * @param nonce the nonce to check in binary form
+ * @return 'true' if timestamp was extracted,
+ *         'false' if nonce does not have valid timestamp.
+ */
+MHD_static_inline_ uint_fast32_t
+get_nonce_timestamp (const uint8_t nonce[mhd_AUTH_DIGEST_NONCE_BIN_SIZE])
+{
+  return (uint_fast32_t)
+         mhd_GET_32BIT_LE_UNALIGN (nonce + mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE);
+}
+
+
+/**
+ * The result of nonce-nc map array check.
+ */
+enum mhd_CheckNonceNC
+{
+  /**
+   * The nonce and NC are OK (valid and NC was not used before).
+   */
+  mhd_CHECK_NONCENC_OK = MHD_DAUTH_OK,
+
+  /**
+   * The 'nonce' is too old, has been overwritten with newer 'nonce' in
+   * the same slot or 'nc' value has been used already.
+   * The validity of the 'nonce' was not be checked.
+   */
+  mhd_CHECK_NONCENC_STALE = MHD_DAUTH_NONCE_STALE,
+
+  /**
+   * The 'nonce' is wrong, it was not generated before.
+   */
+  mhd_CHECK_NONCENC_WRONG = MHD_DAUTH_NONCE_WRONG
+};
+
+
+/**
+ * Check nonce-nc map array with the new nonce counter.
+ *
+ * @param daemon the master daemon object
+ * @param noncelen the length of @a nonce, in characters
+ * @param nonce the pointer that referenced hex nonce, does not need to be
+ *              zero-terminated
+ * @param nc the nonce counter
+ * @param time_now the current timestamp
+ * @return #MHD_DAUTH_NONCENC_OK if successful,
+ *         #MHD_DAUTH_NONCENC_STALE if nonce is stale (or no nonce-nc array
+ *         is available),
+ *         #MHD_DAUTH_NONCENC_WRONG if nonce was not recodered in nonce-nc map
+ *         array, while it should.
+ */
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) enum mhd_CheckNonceNC
+check_nonce_nc (struct MHD_Daemon *restrict d,
+                size_t noncelen,
+                const char *restrict nonce,
+                uint_fast32_t nc,
+                uint_fast32_t time_now)
+{
+  uint8_t nonce_bin[mhd_AUTH_DIGEST_NONCE_BIN_SIZE];
+  struct mhd_DaemonAuthDigestNonceData *nonce_slot;
+  uint_fast32_t valid_time;
+  uint_fast32_t slot_valid_time;
+  enum mhd_CheckNonceNC ret;
+
+  mhd_assert (! mhd_D_HAS_MASTER (d)); /* only master daemon should be used */
+  mhd_assert (0 != noncelen);
+  mhd_assert (0 != nc);
+  if (mhd_AUTH_DIGEST_NONCE_LEN != noncelen)
+    return mhd_CHECK_NONCENC_WRONG;
+
+  if (mhd_AUTH_DIGEST_NONCE_BIN_SIZE !=
+      mhd_hex_to_bin (nonce,
+                      mhd_AUTH_DIGEST_NONCE_LEN,
+                      nonce_bin))
+    return mhd_CHECK_NONCENC_WRONG;
+
+  if ((NULL != memchr (nonce, 'A', mhd_AUTH_DIGEST_NONCE_LEN))
+      || (NULL != memchr (nonce, 'B', mhd_AUTH_DIGEST_NONCE_LEN))
+      || (NULL != memchr (nonce, 'C', mhd_AUTH_DIGEST_NONCE_LEN))
+      || (NULL != memchr (nonce, 'D', mhd_AUTH_DIGEST_NONCE_LEN))
+      || (NULL != memchr (nonce, 'E', mhd_AUTH_DIGEST_NONCE_LEN))
+      || (NULL != memchr (nonce, 'F', mhd_AUTH_DIGEST_NONCE_LEN)))
+    return mhd_CHECK_NONCENC_WRONG;   /* Upper case chars are not produced by MHD */
+
+  valid_time = get_nonce_timestamp (nonce_bin);
+
+  nonce_slot = d->auth_dg.nonces
+               + nonce_to_index (nonce_bin,
+                                 d->auth_dg.cfg.nonces_num);
+
+  mhd_mutex_lock_chk (&(d->auth_dg.nonces_lock));
+
+  slot_valid_time = nonce_slot->valid_time;
+  if ((0 == memcmp (nonce_slot->nonce,
+                    nonce_bin,
+                    sizeof(nonce_slot->nonce)))
+      && (slot_valid_time == valid_time))
+  {
+    /* The nonce matches the stored nonce */
+    if (nonce_slot->max_recvd_nc < nc)
+    {
+      /* 'nc' is larger, shift bitmask and bump limit */
+      const uint_fast32_t jump_size =
+        (uint_fast32_t) nc - nonce_slot->max_recvd_nc;
+      if (64 > jump_size)
+      {
+        /* small jump, less than mask width */
+        nonce_slot->nmask <<= jump_size;
+        /* Set bit for the old 'nc' value */
+        nonce_slot->nmask |= (UINT64_C (1) << (jump_size - 1));
+      }
+      else if (64 == jump_size)
+        nonce_slot->nmask = (UINT64_C (1) << 63);
+      else
+        nonce_slot->nmask = 0; /* big jump, unset all bits in the mask */
+      nonce_slot->max_recvd_nc = nc;
+      ret = mhd_CHECK_NONCENC_OK;
+    }
+    else if (nonce_slot->max_recvd_nc == nc)
+      /* 'nc' was already used */
+      ret = mhd_CHECK_NONCENC_STALE;
+    else /* (nonce_slot->max_recvd_nc > nc) */
+    {
+      /* Out-of-order 'nc' value. Check whether was used before */
+      if (64 <= nonce_slot->max_recvd_nc - nc)
+      {
+        if (0 ==
+            ((UINT64_C (1) << (nonce_slot->max_recvd_nc - nc - 1))
+             & nonce_slot->nmask))
+        {
+          /* 'nc' has not been used before. Set the bit. */
+          nonce_slot->nmask |=
+            (UINT64_C (1) << (nonce_slot->max_recvd_nc - nc - 1));
+          ret = mhd_CHECK_NONCENC_OK;
+        }
+        else
+          ret = mhd_CHECK_NONCENC_STALE; /* 'nc' has been used before */
+      }
+      else
+        ret = mhd_CHECK_NONCENC_STALE; /* 'nc' is too old (more than 64 value before) */
+    }
+  }
+  else
+  {
+    /* The nonce does not match the stored nonce */
+    if (((valid_time - slot_valid_time) & UINT32_C (0xFFFFFFFF)) <=
+        ((slot_valid_time - valid_time) & UINT32_C (0xFFFFFFFF)))
+    {
+      /* The stored nonce was generated before the checked nonce */
+      ret = mhd_CHECK_NONCENC_WRONG;
+    }
+    else
+    {
+      /* The stored nonce was generated after the checked nonce */
+      const uint_fast32_t nonce_gen_time =
+        ((valid_time - d->auth_dg.cfg.nonce_tmout) & UINT32_C (0xFFFFFFFF));
+      if (((time_now - nonce_gen_time) & UINT32_C (0xFFFFFFFF)) <
+          ((nonce_gen_time - time_now) & UINT32_C (0xFFFFFFFF)))
+        ret = mhd_CHECK_NONCENC_WRONG; /* The nonce is generated in "future" */
+      else
+        /* Probably the nonce has been overwritten with a newer nonce */
+        ret = mhd_CHECK_NONCENC_STALE;
+    }
+  }
+
+  mhd_mutex_unlock_chk (&(d->auth_dg.nonces_lock));
+
+  return ret;
+}
+
+
+struct test_header_param
+{
+  struct MHD_Request *request;
+  size_t num_get_params;
+};
+
+/**
+ * Test if the given key-value pair is in the headers for the
+ * given request.
+ *
+ * @param cls the test context
+ * @param name the name of the key
+ * @param value the value of the key
+ * @return 'true' if the key-value pair is in the headers,
+ *         'false' if not
+ */
+static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) bool
+test_header (void *restrict cls,
+             const struct MHD_String *restrict name,
+             const struct MHD_StringNullable *restrict value)
+{
+  struct test_header_param *const param = (struct test_header_param *) cls;
+  struct MHD_Request *req = param->request;
+  struct mhd_RequestField *pos;
+  size_t i;
+
+  param->num_get_params++;
+  i = 0;
+  for (pos = mhd_DLINKEDL_GET_FIRST (req, fields);
+       NULL != pos;
+       pos = mhd_DLINKEDL_GET_NEXT (pos, fields))
+  {
+    if (MHD_VK_GET_ARGUMENT != pos->field.kind)
+      continue;
+    if (++i == param->num_get_params)
+    {
+      if (name->len != pos->field.nv.name.len)
+        return false;
+      if (value->len != pos->field.nv.value.len)
+        return false;
+      if (0 != name->len)
+      {
+        mhd_assert (NULL != name->cstr);
+        mhd_assert (NULL != pos->field.nv.name.cstr);
+        if (0 != memcmp (name->cstr,
+                         pos->field.nv.name.cstr,
+                         name->len))
+          return false;
+      }
+      if (0 != value->len)
+      {
+        mhd_assert (NULL != value->cstr);
+        mhd_assert (NULL != pos->field.nv.value.cstr);
+        if (0 != memcmp (value->cstr,
+                         pos->field.nv.value.cstr,
+                         value->len))
+          return false;
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+
+/**
+ * Check that the arguments given by the client as part
+ * of the authentication header match the arguments we
+ * got as part of the HTTP request URI.
+ *
+ * @param req the request with get arguments to compare against
+ * @param args the copy of argument URI string (after "?" in URI), will be
+ *             modified by this function
+ * @return 'true' if the arguments match,
+ *         'false' if not
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (3)
+MHD_FN_PAR_INOUT_SIZE_ (3, 2) bool
+check_argument_match (struct MHD_Request *restrict req,
+                      size_t args_len,
+                      char *restrict args)
+{
+  struct mhd_RequestField *pos;
+  struct test_header_param param;
+
+  param.request = req;
+  param.num_get_params = 0;
+  if (! mhd_parse_get_args (args_len,
+                            args,
+                            &test_header,
+                            &param))
+    return false;
+
+  /* Check that the number of arguments matches */
+  for (pos = mhd_DLINKEDL_GET_FIRST (req, fields);
+       NULL != pos;
+       pos = mhd_DLINKEDL_GET_NEXT (pos, fields))
+  {
+    if (MHD_VK_GET_ARGUMENT != pos->field.kind)
+      continue;
+    param.num_get_params--;
+  }
+
+  if (0 != param.num_get_params)
+    return false; /* argument count mismatch */
+
+  return true;
+}
+
+
+/**
+ * Check that the URI provided by the client as part
+ * of the authentication header match the real HTTP request URI.
+ *
+ * @param req the request to compare URI
+ * @param uri the copy of URI in the authentication header, should point to
+ *            modifiable buffer at least @a uri_len + 1 characters long,
+ *            will be modified by this function, not valid upon return
+ * @param uri_len the length of the @a uri string in characters
+ * @return boolean true if the URIs match,
+ *         boolean false if not
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (3) bool
+check_uri_match (struct MHD_Request *restrict req,
+                 const size_t uri_len,
+                 char *restrict uri)
+{
+  char *qmark;
+  char *args;
+  size_t url_len; /* The part before '?' char */
+  size_t args_len;
+
+  if (uri_len != req->req_target_len)
+    return false;
+
+  uri[uri_len] = 0;
+  qmark = memchr (uri,
+                  '?',
+                  uri_len);
+  if (NULL != qmark)
+  {
+    *qmark = 0;
+    url_len = (size_t) (qmark - uri);
+  }
+  else
+    url_len = uri_len;
+
+  /* Need to unescape URI before comparing with req->url */
+  url_len = mhd_str_pct_decode_lenient_n (uri,
+                                          url_len,
+                                          uri,
+                                          url_len,
+                                          NULL);
+  if ((url_len != req->url_len) ||
+      (0 != memcmp (uri,
+                    req->url,
+                    url_len)))
+    return false;
+
+  args = (NULL != qmark) ? (qmark + 1) : uri + uri_len;
+  args_len = (size_t) (uri + uri_len - args);
+
+  if (! check_argument_match (req,
+                              args_len,
+                              args))
+    return false;
+
+  return true;
+}
+
+
+/**
+ * The size of the unquoting buffer in stack
+ */
+#define mhd_STATIC_UNQ_BUFFER_SIZE 128
+
+
+/**
+ * Get the pointer to buffer with required size
+ * @param tmp1 the first buffer with fixed size
+ * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer
+ * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
+ * @param required_size the required size in buffer
+ * @return the pointer to the buffer or NULL if failed to allocate buffer with
+ *         requested size
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) char *
+get_buffer_for_size (char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE],
+                     char **restrict ptmp2,
+                     size_t *restrict ptmp2_size,
+                     size_t required_size)
+{
+  mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2));
+  mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size));
+  mhd_assert ((0 == *ptmp2_size) || \
+              (mhd_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size));
+
+  if (required_size <= mhd_STATIC_UNQ_BUFFER_SIZE)
+    return tmp1;
+
+  if (required_size <= *ptmp2_size)
+    return *ptmp2;
+
+  if (required_size > mhd_AUTH_DIGEST_MAX_PARAM_SIZE)
+    return NULL;
+  if (NULL != *ptmp2)
+    free (*ptmp2);
+  *ptmp2 = (char *) malloc (required_size);
+  if (NULL == *ptmp2)
+    *ptmp2_size = 0;
+  else
+    *ptmp2_size = required_size;
+  return *ptmp2;
+}
+
+
+/**
+  * The result of parameter unquoting
+  */
+enum mhd_GetUnqResult
+{
+  mhd_UNQ_OK = MHD_DAUTH_OK,               /**< Got unquoted string */
+  mhd_UNQ_TOO_LARGE = MHD_DAUTH_TOO_LARGE, /**< The string is too large to unquote */
+  mhd_UNQ_OUT_OF_MEM = MHD_DAUTH_ERROR     /**< Out of memory error */
+};
+
+/**
+ * Get Digest authorisation parameter as unquoted string.
+ * @param param the parameter to process
+ * @param[in,out] tmp1 the small buffer in stack
+ * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer
+ * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
+ * @param[out] unquoted the pointer to store the result, NOT zero terminated
+ * @return enum code indicating result of the process
+ */
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_INOUT_ (3) MHD_FN_PAR_INOUT_ (4)
+MHD_FN_PAR_OUT_ (5) enum mhd_GetUnqResult
+get_unquoted_param (const struct mhd_RqDAuthParam *param,
+                    char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE],
+                    char **restrict ptmp2,
+                    size_t *restrict ptmp2_size,
+                    struct mhd_BufferConst *restrict unquoted)
+{
+  char *str;
+  size_t len;
+  mhd_assert (NULL != param->value.cstr);
+  mhd_assert (0 != param->value.len);
+
+  if (! param->quoted)
+  {
+    unquoted->data = param->value.cstr;
+    unquoted->size = param->value.len;
+    return mhd_UNQ_OK;
+  }
+  /* The value is present and is quoted, needs to be copied and unquoted */
+  str = get_buffer_for_size (tmp1,
+                             ptmp2,
+                             ptmp2_size,
+                             param->value.len);
+  if (NULL == str)
+    return (param->value.len > mhd_AUTH_DIGEST_MAX_PARAM_SIZE) ?
+           mhd_UNQ_TOO_LARGE : mhd_UNQ_OUT_OF_MEM;
+
+  len = mhd_str_unquote (param->value.cstr,
+                         param->value.len,
+                         str);
+  unquoted->data = str;
+  unquoted->size = len;
+  mhd_assert (0 != unquoted->size);
+  mhd_assert (unquoted->size < param->value.len);
+  return mhd_UNQ_OK;
+}
+
+
+/**
+ * Get copy of Digest authorisation parameter as unquoted string.
+ * @param param the parameter to process
+ * @param[in,out] tmp1 the small buffer in stack
+ * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer
+ * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
+ * @param[out] unquoted the pointer to store the result, NOT zero terminated,
+ *                      but with enough space to zero-terminate
+ * @return enum code indicating result of the process
+ */
+static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_INOUT_ (3) MHD_FN_PAR_INOUT_ (4)
+MHD_FN_PAR_OUT_ (5) enum mhd_GetUnqResult
+get_unquoted_param_copy (const struct mhd_RqDAuthParam *param,
+                         char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE],
+                         char **restrict ptmp2,
+                         size_t *restrict ptmp2_size,
+                         struct mhd_Buffer *restrict unquoted)
+{
+  mhd_assert (NULL != param->value.cstr);
+  mhd_assert (0 != param->value.len);
+
+  /* The value is present and is quoted, needs to be copied and unquoted */
+  /* Allocate buffer with one more additional byte for zero-termination */
+  unquoted->data =
+    get_buffer_for_size (tmp1,
+                         ptmp2,
+                         ptmp2_size,
+                         param->value.len + 1);
+
+  if (NULL == unquoted->data)
+    return (param->value.len + 1 > mhd_AUTH_DIGEST_MAX_PARAM_SIZE) ?
+           mhd_UNQ_TOO_LARGE : mhd_UNQ_OUT_OF_MEM;
+
+  if (! param->quoted)
+  {
+    memcpy (unquoted->data,
+            param->value.cstr,
+            param->value.len);
+    unquoted->size = param->value.len;
+    return mhd_UNQ_OK;
+  }
+
+  unquoted->size =
+    mhd_str_unquote (param->value.cstr,
+                     param->value.len,
+                     unquoted->data);
+  mhd_assert (0 != unquoted->size);
+  mhd_assert (unquoted->size < param->value.len);
+  return mhd_UNQ_OK;
+}
+
+
+/**
+ * Check whether Digest Auth request parameter is equal to given string
+ * @param param the parameter to check
+ * @param str_len the length of the @a str
+ * @param str the string to compare with, does not need to be zero-terminated
+ * @return true is parameter is equal to the given string,
+ *         false otherwise
+ */
+MHD_static_inline_ MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3,2) bool
+is_param_equal (const struct mhd_RqDAuthParam *restrict param,
+                const size_t str_len,
+                const char *restrict str)
+{
+  mhd_assert (NULL != param->value.cstr);
+  mhd_assert (0 != param->value.len);
+  if (param->quoted)
+    return mhd_str_equal_quoted_bin_n (param->value.cstr,
+                                       param->value.len,
+                                       str,
+                                       str_len);
+  return (str_len == param->value.len) &&
+         (0 == memcmp (str, param->value.cstr, str_len));
+}
+
+
+/**
+ * Check whether Digest Auth request parameter is caseless equal to given string
+ * @param param the parameter to check
+ * @param str_len the length of the @a str
+ * @param str the string to compare with, does not need to be zero-terminated
+ * @return true is parameter is caseless equal to the given string,
+ *         false otherwise
+ */
+MHD_static_inline_ MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3,2) bool
+is_param_equal_caseless (const struct mhd_RqDAuthParam *restrict param,
+                         const size_t str_len,
+                         const char *restrict str)
+{
+  mhd_assert (NULL != param->value.cstr);
+  mhd_assert (0 != param->value.len);
+  if (param->quoted)
+    return mhd_str_equal_caseless_quoted_bin_n (param->value.cstr,
+                                                param->value.len,
+                                                str,
+                                                str_len);
+  return (str_len == param->value.len) &&
+         (mhd_str_equal_caseless_bin_n (str, param->value.cstr, str_len));
+}
+
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not supported in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request new nonce must be
+ * generated and client repeat all requests twice (the first time to get a new
+ * nonce and the second time to perform an authorised request).
+ *
+ * @param req the request handle
+ * @param realm the realm for authorization of the client
+ * @param username the username to be authenticated, must be in clear text
+ *                 even if userhash is used by the client
+ * @param password the password used in the authentication,
+ *                 must be NULL if @a userdigest is not NULL
+ * @param userdigest the precalculated binary hash of the string
+ *                   "username:realm:password",
+ *                   must be NULL if @a password is not NULL
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               unlike #digest_auth_check_all() zero is treated as "no limit"
+ * @param mqop the QOP to use
+ * @param malgo digest algorithms allowed to use, fail if algorithm specified
+ *               by the client is not allowed by this parameter
+ * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer,
+ *                  to be freed if not NULL upon return
+ * @return #MHD_DAUTH_OK if authenticated,
+ *         error code otherwise.
+ * @ingroup authentication
+ */
+static MHD_FN_MUST_CHECK_RESULT_
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (4)  MHD_FN_PAR_CSTR_ (4)
+enum MHD_DigestAuthResult
+digest_auth_check_all_inner (struct MHD_Request *restrict req,
+                             const char *restrict realm,
+                             const char *restrict username,
+                             const char *restrict password,
+                             const uint8_t *restrict userdigest,
+                             uint_fast32_t max_nc,
+                             enum MHD_DigestAuthMultiQOP mqop,
+                             enum MHD_DigestAuthMultiAlgo malgo,
+                             char **pbuf,
+                             struct DigestAlgorithm *da)
+{
+  struct MHD_Daemon *const daemon =
+    mhd_daemon_get_master_daemon (
+      mhd_CNTNR_PTR (req, struct MHD_Connection, rq)->daemon);
+  enum MHD_DigestAuthAlgo c_algo; /**< Client's algorithm */
+  enum MHD_DigestAuthQOP c_qop; /**< Client's QOP */
+  unsigned int digest_size;
+  uint8_t hash1_bin[mhd_MAX_DIGEST];
+  uint8_t hash2_bin[mhd_MAX_DIGEST];
+  uint_fast32_t nc;
+  const struct mhd_AuthDigesReqParams *restrict params;
+  /**
+   * Temporal buffer in stack for unquoting and other needs
+   */
+  char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE];
+  char **const ptmp2 = pbuf;     /**< Temporal malloc'ed buffer for unquoting */
+  size_t tmp2_size; /**< The size of @a tmp2 buffer */
+  struct mhd_BufferConst unquoted;
+  struct mhd_Buffer unq_copy;
+  enum mhd_GetUnqResult unq_res;
+  size_t username_len;
+  size_t realm_len;
+
+  mhd_assert ((NULL == password) != (NULL == userdigest));
+
+  tmp2_size = 0;
+
+  if (1)
+  {
+    enum MHD_StatusCode res;
+
+    res = get_rq_auth_digest_params (req);
+    if (MHD_SC_OK != res)
+    {
+      if (MHD_SC_AUTH_ABSENT == res)
+        return MHD_DAUTH_HEADER_MISSING;
+      else if (MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA == res)
+        return MHD_DAUTH_ERROR;
+      else if (MHD_SC_REQ_AUTH_DATA_BROKEN == res)
+        return MHD_DAUTH_HEADER_BROKEN;
+      else
+        mhd_UNREACHABLE ();
+    }
+    params = req->auth.digest.rqp;
+  }
+  mhd_assert (NULL != params);
+
+  /* ** Initial parameters checks and setup ** */
+  /* Get client's algorithm */
+  c_algo = params->algo;
+  /* Check whether client's algorithm is allowed by function parameter */
+  if (((unsigned int) c_algo) !=
+      (((unsigned int) c_algo) & ((unsigned int) malgo)))
+    return MHD_DAUTH_WRONG_ALGO;
+  /* Check whether client's algorithm is supported */
+  if (0 != (((unsigned int) c_algo) & MHD_DIGEST_AUTH_ALGO_SESSION))
+    return MHD_DAUTH_UNSUPPORTED_ALGO;
+#ifndef MHD_SUPPORT_MD5
+  if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5))
+    return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_MD5 */
+#ifndef MHD_SUPPORT_SHA256
+  if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA256))
+    return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_SHA256 */
+#ifndef MHD_SUPPORT_SHA512_256
+  if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA512_256))
+    return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_SHA512_256 */
+  if (! digest_init_one_time (da, get_base_digest_algo (c_algo)))
+    mhd_UNREACHABLE ();
+  /* Check 'mqop' value */
+  c_qop = params->qop;
+  /* Check whether client's QOP is allowed by function parameter */
+  if (((unsigned int) c_qop) !=
+      (((unsigned int) c_qop) & ((unsigned int) mqop)))
+    return MHD_DAUTH_WRONG_QOP;
+  if (0 != (((unsigned int) c_qop) & MHD_DIGEST_AUTH_QOP_AUTH_INT))
+    return MHD_DAUTH_UNSUPPORTED_QOP;
+
+  digest_size = digest_get_size (da);
+
+  /* ** A quick check for presence of all required parameters ** */
+
+  if ((NULL == params->username.value.cstr) &&
+      (NULL == params->username_ext.value.cstr))
+    return MHD_DAUTH_HEADER_BROKEN;
+  else if ((NULL != params->username.value.cstr) &&
+           (NULL != params->username_ext.value.cstr))
+    return MHD_DAUTH_HEADER_BROKEN; /* Parameters cannot be used together */
+  else if ((NULL != params->username_ext.value.cstr) &&
+           (mhd_DAUTH_EXT_PARAM_MIN_LEN > params->username_ext.value.len))
+    return MHD_DAUTH_HEADER_BROKEN;  /* Broken extended notation */
+  else if (params->userhash && (NULL == params->username.value.cstr))
+    return MHD_DAUTH_HEADER_BROKEN;  /* Userhash cannot be used with extended notation */
+  else if (params->userhash && (digest_size * 2 > params->username.value.len))
+    return MHD_DAUTH_WRONG_USERNAME;  /* Too few chars for correct userhash */
+  else if (params->userhash && (digest_size * 4 < params->username.value.len))
+    return MHD_DAUTH_WRONG_USERNAME;  /* Too many chars for correct userhash */
+
+  if (NULL == params->realm.value.cstr)
+    return MHD_DAUTH_HEADER_BROKEN;
+  else if (((NULL == userdigest) || params->userhash) &&
+           (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len))
+    return MHD_DAUTH_TOO_LARGE; /* Realm is too large and should be used in hash calculations */
+
+  if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
+  {
+    if (NULL == params->nc.value.cstr)
+      return MHD_DAUTH_HEADER_BROKEN;
+    else if (0 == params->nc.value.len)
+      return MHD_DAUTH_HEADER_BROKEN;
+    else if (4 * 8 < params->nc.value.len) /* Four times more than needed */
+      return MHD_DAUTH_HEADER_BROKEN;
+
+    if (NULL == params->cnonce.value.cstr)
+      return MHD_DAUTH_HEADER_BROKEN;
+    else if (0 == params->cnonce.value.len)
+      return MHD_DAUTH_HEADER_BROKEN;
+    else if (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len)
+      return MHD_DAUTH_TOO_LARGE;
+  }
+
+  /* The QOP parameter was checked already */
+
+  if (NULL == params->uri.value.cstr)
+    return MHD_DAUTH_HEADER_BROKEN;
+  else if (0 == params->uri.value.len)
+    return MHD_DAUTH_HEADER_BROKEN;
+  else if (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->uri.value.len)
+    return MHD_DAUTH_TOO_LARGE;
+
+  if (NULL == params->nonce.value.cstr)
+    return MHD_DAUTH_HEADER_BROKEN;
+  else if (0 == params->nonce.value.len)
+    return MHD_DAUTH_HEADER_BROKEN;
+  else if (mhd_AUTH_DIGEST_NONCE_LEN * 2 < params->nonce.value.len)
+    return MHD_DAUTH_NONCE_WRONG;
+
+  if (NULL == params->response.value.cstr)
+    return MHD_DAUTH_HEADER_BROKEN;
+  else if (0 == params->response.value.len)
+    return MHD_DAUTH_HEADER_BROKEN;
+  else if (digest_size * 4 < params->response.value.len)
+    return MHD_DAUTH_RESPONSE_WRONG;
+
+  /* ** Check simple parameters match ** */
+
+  /* Check 'algorithm' */
+  /* The 'algorithm' was checked at the start of the function */
+  /* 'algorithm' valid */
+
+  /* Check 'qop' */
+  /* The 'qop' was checked at the start of the function */
+  /* 'qop' valid */
+
+  /* Check 'realm' */
+  realm_len = strlen (realm);
+  if (! is_param_equal (&params->realm,
+                        realm_len,
+                        realm))
+    return MHD_DAUTH_WRONG_REALM;
+  /* 'realm' valid */
+
+  /* Check 'username' */
+  username_len = strlen (username);
+  if (! params->userhash)
+  {
+    if (NULL != params->username.value.cstr)
+    { /* Username in standard notation */
+      if (! is_param_equal (&params->username, username_len, username))
+        return MHD_DAUTH_WRONG_USERNAME;
+    }
+    else
+    { /* Username in extended notation */
+      char *r_uname;
+      size_t buf_size = params->username_ext.value.len;
+      ssize_t res;
+
+      mhd_assert (NULL != params->username_ext.value.cstr);
+      mhd_assert (mhd_DAUTH_EXT_PARAM_MIN_LEN <= buf_size); /* It was checked already */
+      buf_size += 1; /* For zero-termination */
+      buf_size -= mhd_DAUTH_EXT_PARAM_MIN_LEN;
+      r_uname = get_buffer_for_size (tmp1, ptmp2, &tmp2_size, buf_size);
+      if (NULL == r_uname)
+        return (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < buf_size) ?
+               MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR;
+      res = get_rq_extended_uname_copy_z (params->username_ext.value.cstr,
+                                          params->username_ext.value.len,
+                                          r_uname, buf_size);
+      if (0 > res)
+        return MHD_DAUTH_HEADER_BROKEN; /* Broken extended notation */
+      if ((username_len != (size_t) res) ||
+          (0 != memcmp (username, r_uname, username_len)))
+        return MHD_DAUTH_WRONG_USERNAME;
+    }
+  }
+  else
+  { /* Userhash */
+    mhd_assert (NULL != params->username.value.cstr);
+    calc_userhash (da,
+                   username_len,
+                   username,
+                   realm_len,
+                   realm,
+                   hash1_bin);
+    if (digest_has_error (da))
+      return MHD_DAUTH_ERROR;
+    mhd_assert (sizeof (tmp1) >= (2 * digest_size));
+    mhd_bin_to_hex (hash1_bin, digest_size, tmp1);
+    if (! is_param_equal_caseless (&params->username, 2 * digest_size, tmp1))
+      return MHD_DAUTH_WRONG_USERNAME;
+    /* To simplify the logic, the digest is reset here instead of resetting
+       before the next hash calculation. */
+    digest_reset (da);
+  }
+  /* 'username' valid */
+
+  /* ** Do basic nonce and nonce-counter checks (size, timestamp) ** */
+
+  /* Get 'nc' digital value */
+  nc = 0;
+  switch (get_rq_nc (params,
+                     &nc))
+  {
+  case mhd_GET_RQ_NC_NONE:
+    if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
+      return MHD_DAUTH_HEADER_BROKEN;
+    nc = 1; /* Force 'nc' value */
+    break;
+  case mhd_GET_RQ_NC_VALID:
+    if (MHD_DIGEST_AUTH_QOP_NONE == c_qop)
+      return MHD_DAUTH_HEADER_BROKEN;
+    break;
+  case mhd_GET_RQ_NC_TOO_LONG:
+  case mhd_GET_RQ_NC_TOO_LARGE:
+    return MHD_DAUTH_NONCE_STALE;
+    break;
+  case mhd_GET_RQ_NC_BROKEN:
+    return MHD_DAUTH_HEADER_BROKEN;
+    break;
+  default:
+    mhd_UNREACHABLE ();
+    break;
+  }
+  if (0 == nc)
+    return MHD_DAUTH_HEADER_BROKEN;
+  if (0 == max_nc)
+    max_nc = daemon->auth_dg.cfg.def_max_nc;
+  if (max_nc < nc)
+    return MHD_DAUTH_NONCE_STALE;    /* Too large 'nc' value */
+  /* Got 'nc' digital value */
+
+  /* Get 'nonce' with basic checks */
+  unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
+                                &unquoted);
+  if (mhd_UNQ_TOO_LARGE == unq_res)
+    return MHD_DAUTH_TOO_LARGE;
+  if (mhd_UNQ_OUT_OF_MEM == unq_res)
+    return MHD_DAUTH_ERROR;
+
+
+  switch (check_nonce_nc (daemon,
+                          unquoted.size,
+                          unquoted.data,
+                          nc,
+                          (uint_fast32_t)
+                          ((mhd_monotonic_msec_counter () / 1000)
+                           & UINT32_C (0xFFFFFFFF))))
+  {
+  case mhd_CHECK_NONCENC_OK:
+    break;
+  case mhd_CHECK_NONCENC_STALE:
+    return MHD_DAUTH_NONCE_STALE;
+  case mhd_CHECK_NONCENC_WRONG:
+    return MHD_DAUTH_NONCE_WRONG;
+  default:
+    mhd_UNREACHABLE ();
+    break;
+  }
+  /* The nonce was generated by MHD, is not stale and nonce-nc combination has
+     not been used before */
+
+  /* ** Build H(A2) and check URI match in the header and in the request ** */
+
+  /* Get 'uri' */
+  mhd_assert (! da->hashing);
+  digest_update (da, req->method.len, req->method.cstr);
+  digest_update_with_colon (da);
+#if 0
+  /* TODO: add support for "auth-int" */
+  digest_update_str (da, hentity);
+  digest_update_with_colon (da);
+#endif
+  unq_res = get_unquoted_param_copy (&params->uri, tmp1, ptmp2, &tmp2_size,
+                                     &unq_copy);
+  if (mhd_UNQ_TOO_LARGE == unq_res)
+    return MHD_DAUTH_TOO_LARGE;
+  if (mhd_UNQ_OUT_OF_MEM == unq_res)
+    return MHD_DAUTH_ERROR;
+
+  digest_update_buf (da, &unq_copy);
+  /* The next check will modify copied URI string */
+  if (! check_uri_match (req, unq_copy.size, unq_copy.data))
+    return MHD_DAUTH_WRONG_URI;
+  digest_calc_hash (da, hash2_bin);
+#ifdef mhd_DIGEST_HAS_EXT_ERROR
+  /* Skip digest calculation external error check, the next one checks both */
+#endif /* mhd_DIGEST_HAS_EXT_ERROR */
+  /* Got H(A2) */
+
+  /* ** Build H(A1) ** */
+  if (NULL == userdigest)
+  {
+    mhd_assert (! da->hashing);
+    digest_reset (da);
+    calc_userdigest (da,
+                     username, username_len,
+                     realm, realm_len,
+                     password,
+                     hash1_bin);
+  }
+  /* TODO: support '-sess' versions */
+#ifdef mhd_DIGEST_HAS_EXT_ERROR
+  if (digest_has_error (da))
+    return MHD_DAUTH_ERROR;
+#endif /* mhd_DIGEST_HAS_EXT_ERROR */
+  /* Got H(A1) */
+
+  /* **  Check 'response' ** */
+
+  mhd_assert (! da->hashing);
+  digest_reset (da);
+  /* Update digest with H(A1) */
+  mhd_assert (sizeof (tmp1) >= (digest_size * 2));
+  if (NULL == userdigest)
+    mhd_bin_to_hex (hash1_bin, digest_size, tmp1);
+  else
+    mhd_bin_to_hex (userdigest, digest_size, tmp1);
+  digest_update (da, digest_size * 2, (const uint8_t *) tmp1);
+
+  /* H(A1) is not needed anymore, reuse the buffer.
+   * Use hash1_bin for the client's 'response' decoded to binary form. */
+  unq_res = get_unquoted_param (&params->response, tmp1, ptmp2, &tmp2_size,
+                                &unquoted);
+  if (mhd_UNQ_TOO_LARGE == unq_res)
+    return MHD_DAUTH_TOO_LARGE;
+  if (mhd_UNQ_OUT_OF_MEM == unq_res)
+    return MHD_DAUTH_ERROR;
+  if (digest_size != mhd_hex_to_bin (unquoted.data, unquoted.size, hash1_bin))
+    return MHD_DAUTH_RESPONSE_WRONG;
+
+  /* Update digest with ':' */
+  digest_update_with_colon (da);
+  /* Update digest with 'nonce' text value */
+  unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
+                                &unquoted);
+  if (mhd_UNQ_TOO_LARGE == unq_res)
+    return MHD_DAUTH_TOO_LARGE;
+  if (mhd_UNQ_OUT_OF_MEM == unq_res)
+    return MHD_DAUTH_ERROR;
+  digest_update_cbuf (da, &unquoted);
+  /* Update digest with ':' */
+  digest_update_with_colon (da);
+  if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
+  {
+    /* Update digest with 'nc' text value */
+    unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
+                                  &unquoted);
+    if (mhd_UNQ_TOO_LARGE == unq_res)
+      return MHD_DAUTH_TOO_LARGE;
+    if (mhd_UNQ_OUT_OF_MEM == unq_res)
+      return MHD_DAUTH_ERROR;
+    digest_update_cbuf (da, &unquoted);
+    /* Update digest with ':' */
+    digest_update_with_colon (da);
+    /* Update digest with 'cnonce' value */
+    unq_res = get_unquoted_param (&params->cnonce, tmp1, ptmp2, &tmp2_size,
+                                  &unquoted);
+    if (mhd_UNQ_TOO_LARGE == unq_res)
+      return MHD_DAUTH_TOO_LARGE;
+    if (mhd_UNQ_OUT_OF_MEM == unq_res)
+      return MHD_DAUTH_ERROR;
+    digest_update_cbuf (da, &unquoted);
+    /* Update digest with ':' */
+    digest_update_with_colon (da);
+    /* Update digest with 'qop' value */
+    unq_res = get_unquoted_param (&params->qop_raw, tmp1, ptmp2, &tmp2_size,
+                                  &unquoted);
+    if (mhd_UNQ_TOO_LARGE == unq_res)
+      return MHD_DAUTH_TOO_LARGE;
+    if (mhd_UNQ_OUT_OF_MEM == unq_res)
+      return MHD_DAUTH_ERROR;
+    digest_update_cbuf (da, &unquoted);
+    /* Update digest with ':' */
+    digest_update_with_colon (da);
+  }
+  /* Update digest with H(A2) */
+  mhd_bin_to_hex (hash2_bin, digest_size, tmp1);
+  digest_update (da, digest_size * 2, (const uint8_t *) tmp1);
+
+  /* H(A2) is not needed anymore, reuse the buffer.
+   * Use hash2_bin for the calculated response in binary form */
+  digest_calc_hash (da, hash2_bin);
+#ifdef mhd_DIGEST_HAS_EXT_ERROR
+  if (digest_has_error (da))
+    return MHD_DAUTH_ERROR;
+#endif /* mhd_DIGEST_HAS_EXT_ERROR */
+
+  if (0 != memcmp (hash1_bin, hash2_bin, digest_size))
+    return MHD_DAUTH_RESPONSE_WRONG;
+
+  return MHD_DAUTH_OK;
+}
+
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not supported in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request new nonce must be
+ * generated and client repeat all requests twice (the first time to get a new
+ * nonce and the second time to perform an authorised request).
+ *
+ * @param req the request handle
+ * @param realm the realm for authorization of the client
+ * @param username the username to be authenticated, must be in clear text
+ *                 even if userhash is used by the client
+ * @param password the password used in the authentication,
+ *                 must be NULL if @a userdigest is not NULL
+ * @param userdigest the precalculated binary hash of the string
+ *                   "username:realm:password",
+ *                   must be NULL if @a password is not NULL
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               if set to zero then daemon's default value is used
+ * @param mqop the QOP to use
+ * @param malgo digest algorithms allowed to use, fail if algorithm specified
+ *               by the client is not allowed by this parameter
+ * @return #MHD_DAUTH_OK if authenticated,
+ *         error code otherwise.
+ * @ingroup authentication
+ */
+static enum MHD_DigestAuthResult
+digest_auth_check_all (struct MHD_Request *restrict req,
+                       const char *restrict realm,
+                       const char *restrict username,
+                       const char *restrict password,
+                       const uint8_t *restrict userdigest,
+                       uint_fast32_t max_nc,
+                       enum MHD_DigestAuthMultiQOP mqop,
+                       enum MHD_DigestAuthMultiAlgo malgo)
+{
+  enum MHD_DigestAuthResult res;
+  char *buf;
+  struct DigestAlgorithm da;
+
+  buf = NULL;
+  digest_setup_zero (&da);
+  res = digest_auth_check_all_inner (req,
+                                     realm,
+                                     username,
+                                     password,
+                                     userdigest,
+                                     max_nc,
+                                     mqop,
+                                     malgo,
+                                     &buf,
+                                     &da);
+  digest_deinit (&da);
+  if (NULL != buf)
+    free (buf);
+
+  return res;
+}
+
+
+/**
+ * Authenticates the authorization header sent by the client.
+ *
+ * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
+ * @a mqop and the client uses this mode, then server generated nonces are
+ * used as one-time nonces because nonce-count is not supported in this old RFC.
+ * Communication in this mode is very inefficient, especially if the client
+ * requests several resources one-by-one as for every request a new nonce must
+ * be generated and client repeats all requests twice (first time to get a new
+ * nonce and second time to perform an authorised request).
+ *
+ * @param request the request
+ * @param realm the realm for authorization of the client
+ * @param username the username to be authenticated, must be in clear text
+ *                 even if userhash is used by the client
+ * @param password the password matching the @a username (and the @a realm)
+ * @param nonce_timeout the period of seconds since nonce generation, when
+ *                      the nonce is recognised as valid and not stale;
+ *                      if zero is specified then daemon default value is used.
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               if zero is specified then daemon default value is used.
+ * @param mqop the QOP to use
+ * @param malgo digest algorithms allowed to use, fail if algorithm used
+ *               by the client is not allowed by this parameter
+ * @return #MHD_DAUTH_OK if authenticated,
+ *         the error code otherwise
+ * @ingroup authentication
+ */
+MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4)
+enum MHD_DigestAuthResult
+MHD_digest_auth_check (struct MHD_Request *MHD_RESTRICT request,
+                       const char *MHD_RESTRICT realm,
+                       const char *MHD_RESTRICT username,
+                       const char *MHD_RESTRICT password,
+                       uint_fast32_t max_nc,
+                       enum MHD_DigestAuthMultiQOP mqop,
+                       enum MHD_DigestAuthMultiAlgo malgo)
+{
+  return digest_auth_check_all (request,
+                                realm,
+                                username,
+                                password,
+                                NULL,
+                                max_nc,
+                                mqop,
+                                malgo);
+}
+
+
+MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_CSTR_ (2)
+MHD_FN_PAR_CSTR_ (3)
+MHD_FN_PAR_IN_SIZE_ (5, 4) enum MHD_DigestAuthResult
+MHD_digest_auth_check_digest (struct MHD_Request *MHD_RESTRICT request,
+                              const char *MHD_RESTRICT realm,
+                              const char *MHD_RESTRICT username,
+                              size_t userdigest_size,
+                              const void *MHD_RESTRICT userdigest,
+                              uint_fast32_t max_nc,
+                              enum MHD_DigestAuthMultiQOP mqop,
+                              enum MHD_DigestAuthMultiAlgo malgo)
+{
+  if (1 != (((0 != (malgo & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0)
+            + ((0 != (malgo & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0)
+            + ((0 != (malgo & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0)))
+    return MHD_DAUTH_UNSUPPORTED_ALGO;
+
+#ifndef MHD_SUPPORT_MD5
+  if (0 != (((unsigned int) malgo) & MHD_DIGEST_BASE_ALGO_MD5))
+    return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_MD5 */
+#ifndef MHD_SUPPORT_SHA256
+  if (0 != (((unsigned int) malgo) & MHD_DIGEST_BASE_ALGO_SHA256))
+    return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_SHA256 */
+#ifndef MHD_SUPPORT_SHA512_256
+  if (0 != (((unsigned int) malgo) & MHD_DIGEST_BASE_ALGO_SHA512_256))
+    return MHD_DAUTH_UNSUPPORTED_ALGO;
+#endif /* ! MHD_SUPPORT_SHA512_256 */
+
+  if (digest_get_hash_size ((enum MHD_DigestAuthAlgo) malgo) !=
+      userdigest_size)
+    return MHD_DAUTH_INVALID_USERDIGEST_SIZE;
+
+  return digest_auth_check_all (request,
+                                realm,
+                                username,
+                                NULL,
+                                (const uint8_t *) userdigest,
+                                max_nc,
+                                mqop,
+                                malgo);
+}

+ 85 - 0
src/mhd2/auth_digest.h

@@ -0,0 +1,85 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/auth_digest.h
+ * @brief  The declaration of the Digest Authorization internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_AUTH_DIGEST_H
+#define MHD_AUTH_DIGEST_H 1
+
+#include "mhd_sys_options.h"
+
+#if ! defined(MHD_SUPPORT_AUTH_DIGEST)
+#error Digest Authorization must be enabled
+#endif
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_digest_auth_data.h"
+
+#include "mhd_public_api.h"
+
+struct MHD_Connection; /* forward declaration */
+struct MHD_Request;    /* forward declaration */
+
+/**
+ * Generate new nonce for Digest Auth, put the nonce in text form to the buffer
+ * @param c the connection to use // TODO: replace with daemon object
+ * @param out_buf the output buffer to put the generated nonce,
+ *                NOT zero terminated
+ * @return 'true' if succeed,
+ *         'false' otherwise
+ */
+MHD_INTERNAL bool
+mhd_auth_digest_get_new_nonce (struct MHD_Connection *restrict c,
+                               char out_buf[mhd_AUTH_DIGEST_NONCE_LEN])
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+/**
+ * Find in request and parse Digest Authentication username information
+ * @param req the request to use
+ * @param[out] v_auth_digest_username the pointer to set to the found data
+ * @return #MHD_SC_OK on success,
+ *         error code otherwise
+ */
+MHD_INTERNAL enum MHD_StatusCode
+mhd_request_get_auth_digest_username (
+  struct MHD_Request *restrict req,
+  const struct MHD_AuthDigestUsernameInfo **restrict v_auth_digest_uname)
+MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+/**
+ * Find in request and parse Digest Authentication information
+ * @param req the request to use
+ * @param[out] v_auth_basic_creds the pointer to set to the found data
+ * @return #MHD_SC_OK on success,
+ *         error code otherwise
+ */
+MHD_INTERNAL enum MHD_StatusCode
+mhd_request_get_auth_digest_info (
+  struct MHD_Request *restrict req,
+  const struct MHD_AuthDigestInfo **restrict v_auth_digest_info)
+MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+#endif /* ! MHD_AUTH_DIGEST_H */

+ 6 - 13
src/mhd2/daemon_options.h

@@ -260,31 +260,24 @@ struct DaemonOptions
 
 
   /**
-   * Value for #MHD_D_O_DAUTH_MAP_SIZE.
+   * Value for #MHD_D_O_AUTH_DIGEST_MAP_SIZE.
    * the size of the map array
    */
-  size_t dauth_map_size;
+  size_t auth_digest_map_size;
 
 
   /**
-   * Value for #MHD_D_O_DAUTH_NONCE_BIND_TYPE.
+   * Value for #MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT.
    * FIXME
    */
-  enum MHD_DaemonOptionValueDAuthBindNonce dauth_nonce_bind_type;
+  unsigned int auth_digest_nonce_timeout;
 
 
   /**
-   * Value for #MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT.
+   * Value for #MHD_D_O_AUTH_DIGEST_DEF_MAX_NC.
    * FIXME
    */
-  unsigned int dauth_def_nonce_timeout;
-
-
-  /**
-   * Value for #MHD_D_O_DAUTH_DEF_MAX_NC.
-   * FIXME
-   */
-  uint_fast32_t dauth_def_max_nc;
+  uint_fast32_t auth_digest_def_max_nc;
 
 
 };

+ 6 - 9
src/mhd2/daemon_set_options.c

@@ -228,17 +228,14 @@ MHD_daemon_set_options (
           option->val.random_entropy.v_buf_size;
       }
       continue;
-    case MHD_D_O_DAUTH_MAP_SIZE:
-      settings->dauth_map_size = option->val.dauth_map_size;
+    case MHD_D_O_AUTH_DIGEST_MAP_SIZE:
+      settings->auth_digest_map_size = option->val.auth_digest_map_size;
       continue;
-    case MHD_D_O_DAUTH_NONCE_BIND_TYPE:
-      settings->dauth_nonce_bind_type = option->val.dauth_nonce_bind_type;
+    case MHD_D_O_AUTH_DIGEST_NONCE_TIMEOUT:
+      settings->auth_digest_nonce_timeout = option->val.auth_digest_nonce_timeout;
       continue;
-    case MHD_D_O_DAUTH_DEF_NONCE_TIMEOUT:
-      settings->dauth_def_nonce_timeout = option->val.dauth_def_nonce_timeout;
-      continue;
-    case MHD_D_O_DAUTH_DEF_MAX_NC:
-      settings->dauth_def_max_nc = option->val.dauth_def_max_nc;
+    case MHD_D_O_AUTH_DIGEST_DEF_MAX_NC:
+      settings->auth_digest_def_max_nc = option->val.auth_digest_def_max_nc;
       continue;
     case MHD_D_O_SENTINEL:
     default: /* for -Wswitch-default -Wswitch-enum */ 

+ 133 - 15
src/mhd2/daemon_start.c

@@ -28,6 +28,7 @@
 
 #include "sys_base_types.h"
 #include "sys_malloc.h"
+#include "compat_calloc.h"
 
 #include <string.h>
 #include "sys_sockets_types.h"
@@ -1468,6 +1469,111 @@ dauth_init (struct MHD_Daemon *restrict d,
 
 #endif
 
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+/**
+ * Initialise daemon Digest Auth data
+ * @param d the daemon object
+ * @param s the user settings
+ * @return #MHD_SC_OK on success,
+ *         the error code otherwise
+ */
+static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \
+  MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode
+daemon_init_auth_digest (struct MHD_Daemon *restrict d,
+                         struct DaemonOptions *restrict s)
+{
+  mhd_StatusCodeInt ret;
+  size_t nonces_num;
+
+  if (0 == s->random_entropy.v_buf_size)
+  {
+    /* No initialisation needed */
+#ifndef HAVE_NULL_PTR_ALL_ZEROS
+    d->auth_dg.entropy.data = NULL;
+    d->auth_dg.nonces = NULL;
+#endif
+    return MHD_SC_OK;
+  }
+  nonces_num = s->auth_digest_map_size;
+  if (0 == nonces_num)
+    nonces_num = 1000;
+  d->auth_dg.nonces = (struct mhd_DaemonAuthDigestNonceData *)
+                      mhd_calloc (nonces_num, \
+                                  sizeof(struct mhd_DaemonAuthDigestNonceData));
+  if (NULL == d->auth_dg.nonces)
+  {
+    mhd_LOG_MSG (d, \
+                 MHD_SC_DAEMON_MEM_ALLOC_FAILURE, \
+                 "Failed to allocate memory for Digest Auth array");
+    return MHD_SC_DAEMON_MEM_ALLOC_FAILURE;
+  }
+  d->auth_dg.cfg.nonces_num = nonces_num;
+
+  if (! mhd_mutex_init (&(d->auth_dg.nonces_lock)))
+  {
+    mhd_LOG_MSG (d, MHD_SC_MUTEX_INIT_FAILURE, \
+                 "Failed to initialise mutex for the Digest Auth data");
+    ret = MHD_SC_MUTEX_INIT_FAILURE;
+  }
+  else
+  {
+    if (! mhd_atomic_counter_init (&(d->auth_dg.num_gen_nonces), 0))
+    {
+      mhd_LOG_MSG (d, MHD_SC_MUTEX_INIT_FAILURE, \
+                   "Failed to initialise mutex for the Digest Auth data");
+      ret = MHD_SC_MUTEX_INIT_FAILURE;
+    }
+    else
+    {
+      /* Move ownership of the entropy buffer */
+      d->auth_dg.entropy.data = s->random_entropy.v_buf;
+      d->auth_dg.entropy.size = s->random_entropy.v_buf_size;
+      s->random_entropy.v_buf = NULL;
+      s->random_entropy.v_buf_size = 0;
+
+      d->auth_dg.cfg.nonce_tmout = s->auth_digest_nonce_timeout;
+      if (0 == d->auth_dg.cfg.nonce_tmout)
+        d->auth_dg.cfg.nonce_tmout = MHD_AUTH_DIGEST_DEF_TIMEOUT;
+      d->auth_dg.cfg.def_max_nc = s->auth_digest_def_max_nc;
+      if (0 == d->auth_dg.cfg.def_max_nc)
+        d->auth_dg.cfg.def_max_nc = MHD_AUTH_DIGEST_DEF_MAX_NC;
+
+      return MHD_SC_OK; /* Success exit point */
+    }
+    mhd_mutex_destroy_chk (&(d->auth_dg.nonces_lock));
+  }
+
+  free (d->auth_dg.nonces);
+  mhd_assert (MHD_SC_OK != ret);
+  return ret; /* Failure exit point */
+}
+
+
+/**
+ * Deinitialise daemon Digest Auth data
+ * @param d the daemon object
+ */
+MHD_FN_PAR_NONNULL_ (1) static void
+daemon_deinit_auth_digest (struct MHD_Daemon *restrict d)
+{
+  if (0 == d->auth_dg.entropy.size)
+    return; /* Digest Auth not used, nothing to deinitialise */
+
+  mhd_assert (NULL != d->auth_dg.entropy.data);
+  free (d->auth_dg.entropy.data);
+  mhd_atomic_counter_deinit (&(d->auth_dg.num_gen_nonces));
+  mhd_mutex_destroy_chk (&(d->auth_dg.nonces_lock));
+  mhd_assert (NULL != d->auth_dg.nonces);
+  mhd_assert (0 != d->auth_dg.nonces);
+  free (d->auth_dg.nonces);
+}
+
+
+#else  /* MHD_SUPPORT_AUTH_DIGEST */
+#define daemon_init_auth_digest(d,s)  (MHD_SC_OK)
+#define daemon_deinit_auth_digest(d)  ((void) 0)
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
 
 /**
  * Initialise daemon TLS data
@@ -2420,6 +2526,11 @@ deinit_workers_pool (struct MHD_Daemon *restrict d,
 static MHD_FN_PAR_NONNULL_ (1) void
 reset_master_only_areas (struct MHD_Daemon *restrict d)
 {
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+  memset (&(d->auth_dg.nonces_lock),
+          0x7F,
+          sizeof(d->auth_dg.nonces_lock));
+#endif
   /* Not needed. It is initialised later */
   /* memset (&(d->req_cfg.large_buf), 0, sizeof(d->req_cfg.large_buf)); */
   (void) d;
@@ -2944,34 +3055,39 @@ daemon_start_internal (struct MHD_Daemon *restrict d,
     return res;
 
   mhd_assert (d->dbg.net_inited);
-  res = daemon_init_tls (d, s);
+
+  res = daemon_init_auth_digest (d, s);
 
   if (MHD_SC_OK == res)
   {
-    mhd_assert (d->dbg.tls_inited);
-    res = daemon_init_threading_and_conn (d, s);
+    res = daemon_init_tls (d, s);
     if (MHD_SC_OK == res)
     {
-      mhd_assert (d->dbg.threading_inited);
-      mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type));
-
-      res = daemon_init_large_buf (d, s);
+      mhd_assert (d->dbg.tls_inited);
+      res = daemon_init_threading_and_conn (d, s);
       if (MHD_SC_OK == res)
       {
-        res = daemon_start_threads (d);
+        mhd_assert (d->dbg.threading_inited);
+        mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type));
+
+        res = daemon_init_large_buf (d, s);
         if (MHD_SC_OK == res)
         {
-          return MHD_SC_OK;
-        }
+          res = daemon_start_threads (d);
+          if (MHD_SC_OK == res)
+          {
+            return MHD_SC_OK;
+          }
 
-        /* Below is a clean-up path */
-        daemon_deinit_large_buf (d);
+          /* Below is a clean-up path */
+          daemon_deinit_large_buf (d);
+        }
+        daemon_deinit_threading_and_conn (d);
       }
-      daemon_deinit_threading_and_conn (d);
+      daemon_deinit_tls (d);
     }
-    daemon_deinit_tls (d);
+    daemon_deinit_auth_digest (d);
   }
-
   daemon_deinit_net (d);
   mhd_assert (MHD_SC_OK != res);
   return res;
@@ -3031,6 +3147,8 @@ MHD_daemon_destroy (struct MHD_Daemon *daemon)
 
     daemon_deinit_tls (daemon);
 
+    daemon_deinit_auth_digest (daemon);
+
     daemon_deinit_net (daemon);
   }
   daemon->state = mhd_DAEMON_STATE_STOPPED; /* Useful only for debugging */

+ 8 - 0
src/mhd2/lib_get_info.c

@@ -128,6 +128,14 @@ MHD_lib_get_info_fixed_sz (enum MHD_LibInfoFixed info_type,
 #endif
     return MHD_SC_OK;
   case MHD_LIB_INFO_FIXED_HAS_DIGEST_AUTH:
+    if (sizeof(return_data->v_bool) > return_data_size)
+      return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+    return_data->v_bool = MHD_YES;
+#else
+    return_data->v_bool = MHD_NO;
+#endif
+    return MHD_SC_OK;
   case MHD_LIB_INFO_FIXED_HAS_DIGEST_AUTH_RFC2069:
   case MHD_LIB_INFO_FIXED_TYPE_DIGEST_AUTH_MD5:
   case MHD_LIB_INFO_FIXED_TYPE_DIGEST_AUTH_SHA256:

+ 9 - 9
src/microhttpd/md5_ext.c → src/mhd2/md5_ext.c

@@ -36,7 +36,7 @@
  * @param ctx the calculation context
  */
 void
-MHD_MD5_init_one_time (struct Md5CtxExt *ctx)
+mhd_MD5_init_one_time (struct mhd_Md5CtxExt *ctx)
 {
   ctx->handle = NULL;
   ctx->ext_error = gnutls_hash_init (&ctx->handle,
@@ -65,12 +65,12 @@ MHD_MD5_init_one_time (struct Md5CtxExt *ctx)
  * @param length number of bytes in @a data
  */
 void
-MHD_MD5_update (struct Md5CtxExt *ctx,
-                const uint8_t *data,
-                size_t length)
+mhd_MD5_update (struct mhd_Md5CtxExt *ctx,
+                size_t size,
+                const uint8_t *data)
 {
   if (0 == ctx->ext_error)
-    ctx->ext_error = gnutls_hash (ctx->handle, data, length);
+    ctx->ext_error = gnutls_hash (ctx->handle, data, size);
 }
 
 
@@ -78,11 +78,11 @@ MHD_MD5_update (struct Md5CtxExt *ctx,
  * Finalise MD5 calculation, return digest, reset hash calculation.
  *
  * @param ctx the calculation context
- * @param[out] digest set to the hash, must be #MD5_DIGEST_SIZE bytes
+ * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes
  */
 void
-MHD_MD5_finish_reset (struct Md5CtxExt *ctx,
-                      uint8_t digest[MD5_DIGEST_SIZE])
+mhd_MD5_finish_reset (struct mhd_Md5CtxExt *ctx,
+                      uint8_t digest[mhd_MD5_DIGEST_SIZE])
 {
   if (0 == ctx->ext_error)
     gnutls_hash_output (ctx->handle, digest);
@@ -95,7 +95,7 @@ MHD_MD5_finish_reset (struct Md5CtxExt *ctx,
  * @param ctx the calculation context
  */
 void
-MHD_MD5_deinit (struct Md5CtxExt *ctx)
+mhd_MD5_deinit (struct mhd_Md5CtxExt *ctx)
 {
   if (NULL != ctx->handle)
     gnutls_hash_deinit (ctx->handle, NULL);

+ 112 - 0
src/mhd2/md5_ext.h

@@ -0,0 +1,112 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/md5_ext.h
+ * @brief  Wrapper declarations for MD5 calculation performed by TLS library
+ * @author Karlson2k (Evgeny Grin)
+ */
+#ifndef MHD_MD5_EXT_H
+#define MHD_MD5_EXT_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+/**
+ * Size of MD5 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+#define mhd_MD5_DIGEST_SIZE (16)
+
+/* Actual declaration is in GnuTLS lib header */
+struct hash_hd_st;
+
+/**
+ * Indicates that struct mhd_Md5CtxExt has 'ext_error'
+ */
+#define mhd_MD5_HAS_EXT_ERROR 1
+
+/**
+ * MD5 calculation context
+ */
+struct mhd_Md5CtxExt
+{
+  struct hash_hd_st *handle; /**< Hash calculation handle */
+  int ext_error; /**< Non-zero if external error occurs during init or hashing */
+};
+
+/**
+ * Indicates that mhd_MD5_init_one_time() function is present.
+ */
+#define mhd_MD5_HAS_INIT_ONE_TIME 1
+
+/**
+ * Initialise structure for MD5 calculation, allocate resources.
+ *
+ * This function must not be called more than one time for @a ctx.
+ *
+ * @param ctx the calculation context
+ */
+void
+mhd_MD5_init_one_time (struct mhd_Md5CtxExt *ctx);
+
+
+/**
+ * MD5 process portion of bytes.
+ *
+ * @param ctx the calculation context
+ * @param size number of bytes in @a data
+ * @param data bytes to add to hash
+ */
+void
+mhd_MD5_update (struct mhd_Md5CtxExt *restrict ctx,
+                size_t size,
+                const uint8_t *restrict data);
+
+
+/**
+ * Indicates that mhd_MD5_finish_reset() function is available
+ */
+#define mhd_MD5_HAS_FINISH_RESET 1
+
+/**
+ * Finalise MD5 calculation, return digest, reset hash calculation.
+ *
+ * @param ctx the calculation context
+ * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes
+ */
+void
+mhd_MD5_finish_reset (struct mhd_Md5CtxExt *restrict ctx,
+                      uint8_t digest[mhd_MD5_DIGEST_SIZE]);
+
+/**
+ * Indicates that mhd_MD5_deinit() function is present
+ */
+#define mhd_MD5_HAS_DEINIT 1
+
+/**
+ * Free allocated resources.
+ *
+ * @param ctx the calculation context
+ */
+void
+mhd_MD5_deinit (struct mhd_Md5CtxExt *ctx);
+
+#endif /* MHD_MD5_EXT_H */

+ 121 - 129
src/microhttpd/md5.c → src/mhd2/md5_int.c

@@ -1,44 +1,41 @@
 /*
-     This file is part of GNU libmicrohttpd
-     Copyright (C) 2022-2023 Evgeny Grin (Karlson2k)
-
-     GNU libmicrohttpd is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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, see <http://www.gnu.org/licenses/>.
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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/md5.c
+ * @file src/mhd2/md5_int.c
  * @brief  Calculation of MD5 digest as defined in RFC 1321
  * @author Karlson2k (Evgeny Grin)
  */
 
-#include "md5.h"
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
 
 #include <string.h>
-#ifdef HAVE_MEMORY_H
-#include <memory.h>
-#endif /* HAVE_MEMORY_H */
 #include "mhd_bithelpers.h"
 #include "mhd_assert.h"
 
-/**
- * Initialise structure for MD5 calculation.
- *
- * @param ctx the calculation context
- */
-void
-MHD_MD5_init (struct Md5Ctx *ctx)
+#include "md5_int.h"
+
+MHD_INTERNAL void MHD_FN_PAR_NONNULL_ALL_
+mhd_MD5_init (struct mhd_Md5CtxInt *ctx)
 {
   /* Initial hash values, see RFC 1321, Clause 3.3 (step 3). */
   /* Note: values specified in RFC by bytes and should be loaded in
@@ -54,17 +51,17 @@ MHD_MD5_init (struct Md5Ctx *ctx)
 }
 
 
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
 
 /**
  * Base of MD5 transformation.
  * Gets full 64 bytes block of data and updates hash values;
  * @param H     hash values
- * @param M     the data buffer with #MD5_BLOCK_SIZE bytes block
+ * @param M     the data buffer with #mhd_MD5_BLOCK_SIZE bytes block
  */
-static void
-md5_transform (uint32_t H[MD5_HASH_SIZE_WORDS],
-               const void *M)
+static MHD_FN_PAR_NONNULL_ALL_ void
+md5_transform (uint32_t H[mhd_MD5_HASH_SIZE_WORDS],
+               const void *restrict M)
 {
   /* Working variables,
      See RFC 1321, Clause 3.4 (step 4). */
@@ -76,8 +73,8 @@ md5_transform (uint32_t H[MD5_HASH_SIZE_WORDS],
   /* The data buffer. See RFC 1321, Clause 3.4 (step 4). */
   uint32_t X[16];
 
-#ifndef _MHD_GET_32BIT_LE_UNALIGNED
-  if (0 != (((uintptr_t) M) % _MHD_UINT32_ALIGN))
+#ifndef mhd_GET_32BIT_LE_UNALIGNED
+  if (0 != (((uintptr_t) M) % mhd_UINT32_ALIGN))
   { /* The input data is unaligned. */
     /* Copy the unaligned input data to the aligned buffer. */
     memcpy (X, M, sizeof(X));
@@ -86,7 +83,7 @@ md5_transform (uint32_t H[MD5_HASH_SIZE_WORDS],
      * the next steps. */
     M = (const void *) X;
   }
-#endif /* _MHD_GET_32BIT_LE_UNALIGNED */
+#endif /* mhd_GET_32BIT_LE_UNALIGNED */
 
   /* Four auxiliary functions, see RFC 1321, Clause 3.4 (step 4). */
   /* Some optimisations used. */
@@ -99,7 +96,7 @@ md5_transform (uint32_t H[MD5_HASH_SIZE_WORDS],
 #  define G_FUNC_2(x,y,z) ((z) & (x))
 #else  /* MHD_FAVOR_SMALL_CODE */
 #  define G_FUNC_1(x,y,z) ((((x) ^ (y)) & (z)) ^ (y))
-#  define G_FUNC_2(x,y,z) UINT32_C(0)
+#  define G_FUNC_2(x,y,z) UINT32_C (0)
 #endif /* MHD_FAVOR_SMALL_CODE */
 #define H_FUNC(x,y,z) ((x) ^ (y) ^ (z)) /* Original version */
 /* #define I_FUNC(x,y,z) ((y) ^ ((x) | (~(z)))) */ /* Original version */
@@ -109,45 +106,45 @@ md5_transform (uint32_t H[MD5_HASH_SIZE_WORDS],
      The original function was modified to use X[k] and T[i] as
      direct inputs. */
 #define MD5STEP_R1(va,vb,vc,vd,vX,vs,vT) do {          \
-    (va) += (vX) + (vT);                               \
-    (va) += F_FUNC((vb),(vc),(vd));                    \
-    (va) = _MHD_ROTL32((va),(vs)) + (vb); } while (0)
+          (va) += (vX) + (vT);                               \
+          (va) += F_FUNC ((vb),(vc),(vd));                    \
+          (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0)
 
   /* Get value of X(k) from input data buffer.
      See RFC 1321 Clause 3.4 (step 4). */
 #define GET_X_FROM_DATA(buf,t) \
-  _MHD_GET_32BIT_LE (((const uint32_t*) (buf)) + (t))
+        mhd_GET_32BIT_LE (((const uint32_t*) (buf)) + (t))
 
   /* One step of round 2 of MD5 computation, see RFC 1321, Clause 3.4 (step 4).
      The original function was modified to use X[k] and T[i] as
      direct inputs. */
 #define MD5STEP_R2(va,vb,vc,vd,vX,vs,vT) do {         \
-    (va) += (vX) + (vT);                              \
-    (va) += G_FUNC_1((vb),(vc),(vd));                 \
-    (va) += G_FUNC_2((vb),(vc),(vd));                 \
-    (va) = _MHD_ROTL32((va),(vs)) + (vb); } while (0)
+          (va) += (vX) + (vT);                              \
+          (va) += G_FUNC_1 ((vb),(vc),(vd));                 \
+          (va) += G_FUNC_2 ((vb),(vc),(vd));                 \
+          (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0)
 
   /* One step of round 3 of MD5 computation, see RFC 1321, Clause 3.4 (step 4).
      The original function was modified to use X[k] and T[i] as
      direct inputs. */
 #define MD5STEP_R3(va,vb,vc,vd,vX,vs,vT) do {         \
-    (va) += (vX) + (vT);                              \
-    (va) += H_FUNC((vb),(vc),(vd));                   \
-    (va) = _MHD_ROTL32((va),(vs)) + (vb); } while (0)
+          (va) += (vX) + (vT);                              \
+          (va) += H_FUNC ((vb),(vc),(vd));                   \
+          (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0)
 
   /* One step of round 4 of MD5 computation, see RFC 1321, Clause 3.4 (step 4).
      The original function was modified to use X[k] and T[i] as
      direct inputs. */
 #define MD5STEP_R4(va,vb,vc,vd,vX,vs,vT) do {         \
-    (va) += (vX) + (vT);                              \
-    (va) += I_FUNC((vb),(vc),(vd));                   \
-    (va) = _MHD_ROTL32((va),(vs)) + (vb); } while (0)
+          (va) += (vX) + (vT);                              \
+          (va) += I_FUNC ((vb),(vc),(vd));                   \
+          (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0)
 
 #if ! defined(MHD_FAVOR_SMALL_CODE)
 
   /* Round 1. */
 
-#if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
+#if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
   if ((const void *) X == M)
   {
     /* The input data is already in the data buffer X[] in correct bytes
@@ -173,7 +170,7 @@ md5_transform (uint32_t H[MD5_HASH_SIZE_WORDS],
     MD5STEP_R1 (B, C, D, A, X[15], 22, UINT32_C (0x49b40821));
   }
   else /* Combined with the next 'if' */
-#endif /* _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN */
+#endif /* mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN */
   if (1)
   {
     /* The input data is loaded in correct (little-endian) format before
@@ -382,60 +379,54 @@ md5_transform (uint32_t H[MD5_HASH_SIZE_WORDS],
 }
 
 
-/**
- * Process portion of bytes.
- *
- * @param ctx the calculation context
- * @param data bytes to add to hash
- * @param length number of bytes in @a data
- */
-void
-MHD_MD5_update (struct Md5Ctx *ctx,
-                const uint8_t *data,
-                size_t length)
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) void
+mhd_MD5_update (struct mhd_Md5CtxInt *restrict ctx,
+                size_t size,
+                const uint8_t *restrict data)
 {
   unsigned int bytes_have; /**< Number of bytes in the context buffer */
 
-  mhd_assert ((data != NULL) || (length == 0));
+  mhd_assert ((data != NULL) || (size == 0));
 
 #ifndef MHD_FAVOR_SMALL_CODE
-  if (0 == length)
+  if (0 == size)
     return; /* Shortcut, do nothing */
 #endif /* MHD_FAVOR_SMALL_CODE */
 
-  /* Note: (count & (MD5_BLOCK_SIZE-1))
-           equals (count % MD5_BLOCK_SIZE) for this block size. */
-  bytes_have = (unsigned int) (ctx->count & (MD5_BLOCK_SIZE - 1));
-  ctx->count += length;
+  /* Note: (count & (mhd_MD5_BLOCK_SIZE-1))
+           equals (count % mhd_MD5_BLOCK_SIZE) for this block size. */
+  bytes_have = (unsigned int) (ctx->count & (mhd_MD5_BLOCK_SIZE - 1));
+  ctx->count += size;
 
   if (0 != bytes_have)
   {
-    unsigned int bytes_left = MD5_BLOCK_SIZE - bytes_have;
-    if (length >= bytes_left)
+    unsigned int bytes_left = mhd_MD5_BLOCK_SIZE - bytes_have;
+    if (size >= bytes_left)
     {     /* Combine new data with data in the buffer and
              process the full block. */
       memcpy (((uint8_t *) ctx->buffer) + bytes_have,
               data,
               bytes_left);
       data += bytes_left;
-      length -= bytes_left;
+      size -= bytes_left;
       md5_transform (ctx->H, ctx->buffer);
       bytes_have = 0;
     }
   }
 
-  while (MD5_BLOCK_SIZE <= length)
+  while (mhd_MD5_BLOCK_SIZE <= size)
   {   /* Process any full blocks of new data directly,
          without copying to the buffer. */
     md5_transform (ctx->H, data);
-    data += MD5_BLOCK_SIZE;
-    length -= MD5_BLOCK_SIZE;
+    data += mhd_MD5_BLOCK_SIZE;
+    size -= mhd_MD5_BLOCK_SIZE;
   }
 
-  if (0 != length)
+  if (0 != size)
   {   /* Copy incomplete block of new data (if any)
          to the buffer. */
-    memcpy (((uint8_t *) ctx->buffer) + bytes_have, data, length);
+    memcpy (((uint8_t *) ctx->buffer) + bytes_have, data, size);
   }
 }
 
@@ -451,15 +442,10 @@ MHD_MD5_update (struct Md5Ctx *ctx,
  */
 #define MD5_SIZE_OF_LEN_ADD (MD5_SIZE_OF_LEN_ADD_BITS / 8)
 
-/**
- * Finalise MD5 calculation, return digest.
- *
- * @param ctx the calculation context
- * @param[out] digest set to the hash, must be #MD5_DIGEST_SIZE bytes
- */
-void
-MHD_MD5_finish (struct Md5Ctx *ctx,
-                uint8_t digest[MD5_DIGEST_SIZE])
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_ (2) void
+mhd_MD5_finish (struct mhd_Md5CtxInt *restrict ctx,
+                uint8_t digest[mhd_MD5_DIGEST_SIZE])
 {
   uint64_t num_bits;   /**< Number of processed bits */
   unsigned int bytes_have; /**< Number of bytes in the context buffer */
@@ -469,9 +455,9 @@ MHD_MD5_finish (struct Md5Ctx *ctx,
      not change the amount of hashed data. */
   num_bits = ctx->count << 3;
 
-  /* Note: (count & (MD5_BLOCK_SIZE-1))
-           equals (count % MD5_BLOCK_SIZE) for this block size. */
-  bytes_have = (unsigned int) (ctx->count & (MD5_BLOCK_SIZE - 1));
+  /* Note: (count & (mhd_MD5_BLOCK_SIZE-1))
+           equals (count % mhd_MD5_BLOCK_SIZE) for this block size. */
+  bytes_have = (unsigned int) (ctx->count & (mhd_MD5_BLOCK_SIZE - 1));
 
   /* Input data must be padded with a single bit "1", then with zeros and
      the finally the length of data in bits must be added as the final bytes
@@ -484,12 +470,12 @@ MHD_MD5_finish (struct Md5Ctx *ctx,
      processed immediately). */
   ((uint8_t *) ctx->buffer)[bytes_have++] = 0x80;
 
-  if (MD5_BLOCK_SIZE - bytes_have < MD5_SIZE_OF_LEN_ADD)
+  if (mhd_MD5_BLOCK_SIZE - bytes_have < MD5_SIZE_OF_LEN_ADD)
   {   /* No space in the current block to put the total length of message.
          Pad the current block with zeros and process it. */
-    if (bytes_have < MD5_BLOCK_SIZE)
+    if (bytes_have < mhd_MD5_BLOCK_SIZE)
       memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
-              MD5_BLOCK_SIZE - bytes_have);
+              mhd_MD5_BLOCK_SIZE - bytes_have);
     /* Process the full block. */
     md5_transform (ctx->H, ctx->buffer);
     /* Start the new block. */
@@ -498,54 +484,60 @@ MHD_MD5_finish (struct Md5Ctx *ctx,
 
   /* Pad the rest of the buffer with zeros. */
   memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
-          MD5_BLOCK_SIZE - MD5_SIZE_OF_LEN_ADD - bytes_have);
+          mhd_MD5_BLOCK_SIZE - MD5_SIZE_OF_LEN_ADD - bytes_have);
   /* Put the number of bits in processed data as little-endian value.
      See RFC 1321, clauses 2 and 3.2 (step 2). */
-  _MHD_PUT_64BIT_LE_SAFE (ctx->buffer + MD5_BLOCK_SIZE_WORDS - 2,
-                          num_bits);
+  mhd_PUT_64BIT_LE_UNALIGN (ctx->buffer + mhd_MD5_BLOCK_SIZE_WORDS - 2,
+                            num_bits);
   /* Process the full final block. */
   md5_transform (ctx->H, ctx->buffer);
 
   /* Put in LE mode the hash as the final digest.
      See RFC 1321, clauses 2 and 3.5 (step 5). */
-#ifndef _MHD_PUT_32BIT_LE_UNALIGNED
-  if (1
-#ifndef MHD_FAVOR_SMALL_CODE
-      && (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN)
-#endif /* MHD_FAVOR_SMALL_CODE */
-      )
-  {
-    /* If storing of the final result requires aligned address and
-       the destination address is not aligned or compact code is used,
-       store the final digest in aligned temporary buffer first, then
-       copy it to the destination. */
-    uint32_t alig_dgst[MD5_DIGEST_SIZE_WORDS];
-    _MHD_PUT_32BIT_LE (alig_dgst + 0, ctx->H[0]);
-    _MHD_PUT_32BIT_LE (alig_dgst + 1, ctx->H[1]);
-    _MHD_PUT_32BIT_LE (alig_dgst + 2, ctx->H[2]);
-    _MHD_PUT_32BIT_LE (alig_dgst + 3, ctx->H[3]);
-    /* Copy result to the unaligned destination address. */
-    memcpy (digest, alig_dgst, MD5_DIGEST_SIZE);
-  }
-#ifndef MHD_FAVOR_SMALL_CODE
-  else /* Combined with the next 'if' */
-#endif /* MHD_FAVOR_SMALL_CODE */
-#endif /* ! _MHD_PUT_32BIT_LE_UNALIGNED */
-#if ! defined(MHD_FAVOR_SMALL_CODE) || defined(_MHD_PUT_32BIT_LE_UNALIGNED)
   if (1)
   {
-    /* Use cast to (void*) here to mute compiler alignment warnings.
-     * Compilers are not smart enough to see that alignment has been checked. */
-    _MHD_PUT_32BIT_LE ((void *) (digest + 0 * MD5_BYTES_IN_WORD), ctx->H[0]);
-    _MHD_PUT_32BIT_LE ((void *) (digest + 1 * MD5_BYTES_IN_WORD), ctx->H[1]);
-    _MHD_PUT_32BIT_LE ((void *) (digest + 2 * MD5_BYTES_IN_WORD), ctx->H[2]);
-    _MHD_PUT_32BIT_LE ((void *) (digest + 3 * MD5_BYTES_IN_WORD), ctx->H[3]);
+    bool use_tmp_buf_to_align_result;
+
+#if defined(mhd_PUT_32BIT_LE_UNALIGNED)
+    use_tmp_buf_to_align_result = false;
+#elif defined (MHD_FAVOR_SMALL_CODE)
+    use_tmp_buf_to_align_result = true; /* smaller code: eliminated branch below */
+#else
+    use_tmp_buf_to_align_result =
+      (0 != ((uintptr_t) digest) % mhd_UINT32_ALIGN);
+#endif
+    if (use_tmp_buf_to_align_result)
+    {
+      /* If storing of the final result requires aligned address and
+         the destination address is not aligned or compact code is used,
+         store the final digest in aligned temporary buffer first, then
+         copy it to the destination. */
+      uint32_t alig_dgst[mhd_MD5_DIGEST_SIZE_WORDS];
+      mhd_PUT_32BIT_LE (alig_dgst + 0, ctx->H[0]);
+      mhd_PUT_32BIT_LE (alig_dgst + 1, ctx->H[1]);
+      mhd_PUT_32BIT_LE (alig_dgst + 2, ctx->H[2]);
+      mhd_PUT_32BIT_LE (alig_dgst + 3, ctx->H[3]);
+      /* Copy result to the unaligned destination address. */
+      memcpy (digest, alig_dgst, mhd_MD5_DIGEST_SIZE);
+    }
+    else
+    {
+      /* Use cast to (void*) here to mute compiler alignment warnings.
+       * Compilers are not smart enough to see that alignment has been checked. */
+      mhd_PUT_32BIT_LE ((void *) (digest + 0 * mhd_MD5_BYTES_IN_WORD), \
+                        ctx->H[0]);
+      mhd_PUT_32BIT_LE ((void *) (digest + 1 * mhd_MD5_BYTES_IN_WORD), \
+                        ctx->H[1]);
+      mhd_PUT_32BIT_LE ((void *) (digest + 2 * mhd_MD5_BYTES_IN_WORD), \
+                        ctx->H[2]);
+      mhd_PUT_32BIT_LE ((void *) (digest + 3 * mhd_MD5_BYTES_IN_WORD), \
+                        ctx->H[3]);
+    }
   }
-#endif /* ! MHD_FAVOR_SMALL_CODE || _MHD_PUT_32BIT_LE_UNALIGNED */
 
   /* Erase potentially sensitive data. */
-  memset (ctx, 0, sizeof(struct Md5Ctx));
+  memset (ctx, 0, sizeof(struct mhd_Md5CtxInt));
 }
 
 
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE

+ 129 - 0
src/mhd2/md5_int.h

@@ -0,0 +1,129 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/md5_int.h
+ * @brief  Calculation of MD5 digest
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_MD5_INT_H
+#define MHD_MD5_INT_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+/**
+ * Number of bits in single MD5 word.
+ */
+#define mhd_MD5_WORD_SIZE_BITS (32)
+
+/**
+ * Number of bytes in single MD5 word.
+ */
+#define mhd_MD5_BYTES_IN_WORD (mhd_MD5_WORD_SIZE_BITS / 8)
+
+/**
+ * Hash is kept internally as four 32-bit words.
+ * This is intermediate hash size, used during computing the final digest.
+ */
+#define mhd_MD5_HASH_SIZE_WORDS (4)
+
+/**
+ * Size of MD5 resulting digest in bytes.
+ * This is the final digest size, not intermediate hash.
+ */
+#define mhd_MD5_DIGEST_SIZE_WORDS mhd_MD5_HASH_SIZE_WORDS
+
+/**
+ * Size of MD5 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+#define mhd_MD5_DIGEST_SIZE (mhd_MD5_DIGEST_SIZE_WORDS * mhd_MD5_BYTES_IN_WORD)
+
+/**
+ * Size of MD5 single processing block in bits.
+ */
+#define mhd_MD5_BLOCK_SIZE_BITS 512
+
+/**
+ * Size of MD5 single processing block in bytes.
+ */
+#define mhd_MD5_BLOCK_SIZE (mhd_MD5_BLOCK_SIZE_BITS / 8)
+
+/**
+ * Size of MD5 single processing block in words.
+ */
+#define mhd_MD5_BLOCK_SIZE_WORDS \
+        (mhd_MD5_BLOCK_SIZE_BITS / mhd_MD5_WORD_SIZE_BITS)
+
+
+/**
+ * MD5 calculation context
+ */
+struct mhd_Md5CtxInt
+{
+  uint32_t H[mhd_MD5_HASH_SIZE_WORDS];     /**< Intermediate hash value / digest at end of calculation */
+  uint32_t buffer[mhd_MD5_BLOCK_SIZE_WORDS];   /**< MD5 input data buffer */
+  uint64_t count;                          /**< number of bytes, mod 2^64 */
+};
+
+/**
+ * Initialise structure for MD5 calculation.
+ *
+ * @param ctx the calculation context
+ */
+MHD_INTERNAL void
+mhd_MD5_init (struct mhd_Md5CtxInt *ctx)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * MD5 process portion of bytes.
+ *
+ * @param ctx the calculation context
+ * @param size number of bytes in @a data
+ * @param data bytes to add to hash
+ */
+MHD_INTERNAL void
+mhd_MD5_update (struct mhd_Md5CtxInt *restrict ctx,
+                size_t size,
+                const uint8_t *restrict data)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3, 2);
+
+
+/**
+ * Finalise MD5 calculation, return digest.
+ *
+ * @param ctx the calculation context
+ * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes
+ */
+MHD_INTERNAL void
+mhd_MD5_finish (struct mhd_Md5CtxInt *restrict ctx,
+                uint8_t digest[mhd_MD5_DIGEST_SIZE])
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+/**
+ * Indicates that function mhd_MD5_finish() (without context reset) is available
+ */
+#define mhd_MD5_HAS_FINISH 1
+
+#endif /* MHD_MD5_INT_H */

+ 91 - 0
src/mhd2/mhd_align.h

@@ -0,0 +1,91 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2021-2022 Karlson2k (Evgeny Grin)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_align.h
+ * @brief  types alignment macros
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_ALIGN_H
+#define MHD_ALIGN_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+#include "sys_offsetof.h"
+
+#ifdef HAVE_C_ALIGNOF
+#  ifdef HAVE_STDALIGN_H
+#    include <stdalign.h>
+#  endif /* HAVE_STDALIGN_H */
+#  define mhd_ALIGNOF(type) alignof(type)
+#endif /* HAVE_C_ALIGNOF */
+
+#ifndef mhd_ALIGNOF
+#  if defined(_MSC_VER) && ! defined(__clang__) && _MSC_VER >= 1700
+#    define mhd_ALIGNOF(type) __alignof (type)
+#  endif /* _MSC_VER >= 1700 */
+#endif /* !mhd_ALIGNOF */
+
+#ifdef mhd_ALIGNOF
+#  if (defined(__GNUC__) && __GNUC__ < 4 && __GNUC_MINOR__ < 9 && \
+  ! defined(__clang__)) || \
+  (defined(__clang__) && __clang_major__ < 8) || \
+  (defined(__clang__) && __clang_major__ < 11 && \
+  defined(__apple_build_version__))
+/* GCC before 4.9 and clang before 8.0 have incorrect implementation of 'alignof()'
+   which returns preferred alignment instead of minimal required alignment */
+#    define mhd_ALIGNOF_UNRELIABLE 1
+#  endif
+
+#  if defined(_MSC_VER) && ! defined(__clang__) && _MSC_VER < 1900
+/* MSVC has the same problem as old GCC versions:
+   '__alignof()' may return "preferred" alignment instead of "required". */
+#    define mhd_ALIGNOF_UNRELIABLE 1
+#  endif /* _MSC_VER < 1900 */
+#endif /* mhd_ALIGNOF */
+
+
+/* Provide a limited set of alignment macros */
+/* The set could be extended as needed */
+#if defined(mhd_ALIGNOF) && ! defined(mhd_ALIGNOF_UNRELIABLE)
+#  define mhd_UINT32_ALIGN mhd_ALIGNOF (uint32_t)
+#  define mhd_UINT64_ALIGN mhd_ALIGNOF (uint64_t)
+#else  /* ! mhd_ALIGNOF */
+struct mhd_dummy_uint32_offset_test_
+{
+  char dummy;
+  uint32_t ui32;
+};
+#  define mhd_UINT32_ALIGN \
+        offsetof (struct mhd_dummy_uint32_offset_test_, ui32)
+
+struct mhd_dummy_uint64_offset_test_
+{
+  char dummy;
+  uint64_t ui64;
+};
+#  define mhd_UINT64_ALIGN \
+        offsetof (struct mhd_dummy_uint64_offset_test_, ui64)
+#endif /* ! mhd_ALIGNOF */
+
+#endif /* ! MHD_ALIGN_H */

+ 17 - 0
src/mhd2/mhd_atomic_counter.c

@@ -47,6 +47,23 @@ mhd_atomic_counter_inc_get (struct mhd_AtomicCounter *pcnt)
 }
 
 
+#ifndef NDEBUG
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_inc_wrap_get (struct mhd_AtomicCounter *pcnt)
+{
+  mhd_ATOMIC_COUNTER_TYPE ret;
+
+  mhd_mutex_lock_chk (&(pcnt->lock));
+  ret = ++(pcnt->count);
+  mhd_mutex_unlock_chk (&(pcnt->lock));
+
+  return ret;
+}
+
+
+#endif /* ! NDEBUG */
+
+
 MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
 mhd_atomic_counter_dec_get (struct mhd_AtomicCounter *pcnt)
 {

+ 20 - 0
src/mhd2/mhd_atomic_counter.h

@@ -137,6 +137,26 @@ struct mhd_AtomicCounter
 MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
 mhd_atomic_counter_inc_get (struct mhd_AtomicCounter *pcnt);
 
+#ifdef NDEBUG
+/**
+ * Atomically increment the value of the counter and return the result.
+ * The value is allowed to overflow and get back to zero.
+ * @param pcnt the pointer to the counter to increment
+ * @return the final/resulting counter value
+ */
+#define mhd_atomic_counter_inc_wrap_get(pcnt) mhd_atomic_counter_inc_get (pcnt)
+#else
+/**
+ * Atomically increment the value of the counter and return the result.
+ * The value is allowed to overflow and get back to zero.
+ * @param pcnt the pointer to the counter to increment
+ * @return the final/resulting counter value
+ */
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_inc_wrap_get (struct mhd_AtomicCounter *pcnt);
+
+#endif
+
 /**
  * Atomically decrement the value of the counter and return the result
  * @param pcnt the pointer to the counter to decrement

+ 425 - 0
src/mhd2/mhd_bithelpers.h

@@ -0,0 +1,425 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2019-2023 Karlson2k (Evgeny Grin)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_bithelpers.h
+ * @brief  macros for bits manipulations
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_BITHELPERS_H
+#define MHD_BITHELPERS_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+  defined(__OPTIMIZE__)))
+/* Declarations for VC & Clang/C2 built-ins */
+#include <intrin.h>
+#endif /* _MSC_FULL_VER  */
+#include "mhd_byteorder.h"
+#if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN || mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#include "mhd_align.h"
+#endif /* mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN ||
+          mhd_BYTE_ORDER == mhd_BIG_ENDIAN */
+
+#ifndef __has_builtin
+#  define mhd_HAS_BUILTIN(x) (0)
+#else
+#  define mhd_HAS_BUILTIN(x) __has_builtin (x)
+#endif
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
+
+#ifdef MHD_HAVE___BUILTIN_BSWAP32
+#  define mhd_BYTES_SWAP32(value32)  \
+        ((uint32_t) __builtin_bswap32 ((uint32_t) value32))
+#elif defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+  defined(__OPTIMIZE__)))
+/* Clang/C2 may not inline this function if optimizations are turned off. */
+#  ifndef __clang__
+#    pragma intrinsic(_byteswap_ulong)
+#  endif /* ! __clang__ */
+#  define mhd_BYTES_SWAP32(value32)  \
+        ((uint32_t) _byteswap_ulong ((uint32_t) value32))
+#elif \
+  mhd_HAS_BUILTIN (__builtin_bswap32)
+#  define mhd_BYTES_SWAP32(value32)  \
+        ((uint32_t)__builtin_bswap32 ((uint32_t) value32))
+#else  /* ! mhd_HAS_BUILTIN(__builtin_bswap32) */
+#  define mhd_BYTES_SWAP32(value32)                                  \
+        ( (((uint32_t) (value32)) << 24)                                  \
+          | ((((uint32_t) (value32)) & ((uint32_t) 0x0000FF00)) << 8)     \
+          | ((((uint32_t) (value32)) & ((uint32_t) 0x00FF0000)) >> 8)     \
+          | (((uint32_t) (value32))                           >> 24) )
+#endif /* ! mhd_HAS_BUILTIN(__builtin_bswap32) */
+
+#ifdef MHD_HAVE___BUILTIN_BSWAP64
+#  define mhd_BYTES_SWAP64(value64) \
+        ((uint64_t)__builtin_bswap64 ((uint64_t) value64))
+#elif defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+  defined(__OPTIMIZE__)))
+/* Clang/C2 may not inline this function if optimizations are turned off. */
+#  ifndef __clang__
+#    pragma intrinsic(_byteswap_uint64)
+#  endif /* ! __clang__ */
+#  define mhd_BYTES_SWAP64(value64)  \
+        ((uint64_t)_byteswap_uint64 ((uint64_t) value64))
+#elif \
+  mhd_HAS_BUILTIN (__builtin_bswap64)
+#  define mhd_BYTES_SWAP64(value64) \
+        ((uint64_t)__builtin_bswap64 ((uint64_t) value64))
+#else  /* ! mhd_HAS_BUILTIN(__builtin_bswap64) */
+#  define mhd_BYTES_SWAP64(value64)                                         \
+        ( (((uint64_t) (value64)) << 56)                                          \
+          | ((((uint64_t) (value64)) & ((uint64_t) 0x000000000000FF00)) << 40)    \
+          | ((((uint64_t) (value64)) & ((uint64_t) 0x0000000000FF0000)) << 24)    \
+          | ((((uint64_t) (value64)) & ((uint64_t) 0x00000000FF000000)) << 8)     \
+          | ((((uint64_t) (value64)) & ((uint64_t) 0x000000FF00000000)) >> 8)     \
+          | ((((uint64_t) (value64)) & ((uint64_t) 0x0000FF0000000000)) >> 24)    \
+          | ((((uint64_t) (value64)) & ((uint64_t) 0x00FF000000000000)) >> 40)    \
+          | (((uint64_t) (value64))                                   >> 56) )
+#endif /* ! mhd_HAS_BUILTIN(__builtin_bswap64) */
+
+
+/* mhd_PUT_64BIT_LE (addr, value64)
+ * put native-endian 64-bit value64 to addr
+ * in little-endian mode.
+ */
+/* Slow version that works with unaligned addr and with any bytes order */
+#define mhd_PUT_64BIT_LE_SLOW(addr, value64) do {                       \
+          ((uint8_t*) (addr))[0] = (uint8_t) ((uint64_t) (value64));           \
+          ((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 8);    \
+          ((uint8_t*) (addr))[2] = (uint8_t) (((uint64_t) (value64)) >> 16);   \
+          ((uint8_t*) (addr))[3] = (uint8_t) (((uint64_t) (value64)) >> 24);   \
+          ((uint8_t*) (addr))[4] = (uint8_t) (((uint64_t) (value64)) >> 32);   \
+          ((uint8_t*) (addr))[5] = (uint8_t) (((uint64_t) (value64)) >> 40);   \
+          ((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 48);   \
+          ((uint8_t*) (addr))[7] = (uint8_t) (((uint64_t) (value64)) >> 56);   \
+} while (0)
+#if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+#  define mhd_PUT_64BIT_LE(addr, value64)             \
+        ((*(uint64_t*) (addr)) = (uint64_t) (value64))
+#elif mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#  define mhd_PUT_64BIT_LE(addr, value64)             \
+        ((*(uint64_t*) (addr)) = mhd_BYTES_SWAP64 (value64))
+#else  /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+#  define mhd_PUT_64BIT_LE(addr, value64) \
+        mhd_PUT_64BIT_LE_SLOW ((addr),(value64))
+/* Indicate that mhd_PUT_64BIT_LE does not need aligned pointer */
+#  define mhd_PUT_64BIT_LE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+
+/* Put result safely to unaligned address */
+MHD_static_inline_ void
+mhd_PUT_64BIT_LE_UNALIGN (void *dst, uint64_t value)
+{
+#ifndef mhd_PUT_64BIT_LE_UNALIGNED
+  if (0 != ((uintptr_t) dst) % (mhd_UINT64_ALIGN))
+    mhd_PUT_64BIT_LE_SLOW (dst, value);
+  else
+#endif /* ! mhd_PUT_64BIT_LE_UNALIGNED */
+  mhd_PUT_64BIT_LE (dst, value);
+}
+
+
+/* mhd_PUT_32BIT_LE (addr, value32)
+ * put native-endian 32-bit value32 to addr
+ * in little-endian mode.
+ */
+/* Slow version that works with unaligned addr and with any bytes order */
+#define mhd_PUT_32BIT_LE_SLOW(addr, value32) do {                            \
+          ((uint8_t*) (addr))[0] = (uint8_t) ((uint32_t) (value32));           \
+          ((uint8_t*) (addr))[1] = (uint8_t) (((uint32_t) (value32)) >> 8);    \
+          ((uint8_t*) (addr))[2] = (uint8_t) (((uint32_t) (value32)) >> 16);   \
+          ((uint8_t*) (addr))[3] = (uint8_t) (((uint32_t) (value32)) >> 24);   \
+} while (0)
+#if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+#  define mhd_PUT_32BIT_LE(addr,value32)             \
+        ((*(uint32_t*) (addr)) = (uint32_t) (value32))
+#elif mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#  define mhd_PUT_32BIT_LE(addr, value32)            \
+        ((*(uint32_t*) (addr)) = mhd_BYTES_SWAP32 (value32))
+#else  /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+#  define mhd_PUT_32BIT_LE(addr, value32) \
+        mhd_PUT_32BIT_LE_SLOW ((addr),(value32))
+/* Indicate that mhd_PUT_32BIT_LE does not need aligned pointer */
+#  define mhd_PUT_32BIT_LE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+
+/* Put result safely to unaligned address */
+MHD_static_inline_ void
+mhd_PUT_32BIT_LE_UNALIGN (void *dst, uint32_t value)
+{
+#ifndef mhd_PUT_32BIT_LE_UNALIGNED
+  if (0 != ((uintptr_t) dst) % (mhd_UINT32_ALIGN))
+    mhd_PUT_32BIT_LE_SLOW (dst, value);
+  else
+#endif /* ! mhd_PUT_64BIT_LE_UNALIGNED */
+  mhd_PUT_32BIT_LE (dst, value);
+}
+
+
+/* mhd_GET_32BIT_LE (addr)
+ * get little-endian 32-bit value storied at addr
+ * and return it in native-endian mode.
+ */
+/* Slow version that works with unaligned addr and with any bytes order */
+#define mhd_GET_32BIT_LE_SLOW(addr)                             \
+        ( ( (uint32_t) (((const uint8_t*) addr)[0]))            \
+          | (((uint32_t) (((const uint8_t*) addr)[1])) << 8)    \
+          | (((uint32_t) (((const uint8_t*) addr)[2])) << 16)   \
+          | (((uint32_t) (((const uint8_t*) addr)[3])) << 24) )
+#if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+#  define mhd_GET_32BIT_LE(addr)             \
+        (*(const uint32_t*) (addr))
+#elif mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#  define mhd_GET_32BIT_LE(addr)             \
+        mhd_BYTES_SWAP32 (*(const uint32_t*) (addr))
+#else  /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+#  define mhd_GET_32BIT_LE(addr)                          \
+        ( ( (uint32_t) (((const uint8_t*) addr)[0]))            \
+          | (((uint32_t) (((const uint8_t*) addr)[1])) << 8)    \
+          | (((uint32_t) (((const uint8_t*) addr)[2])) << 16)   \
+          | (((uint32_t) (((const uint8_t*) addr)[3])) << 24) )
+/* Indicate that mhd_GET_32BIT_LE does not need aligned pointer */
+#  define mhd_GET_32BIT_LE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_BIG_ENDIAN */
+
+/* Get value safely from an unaligned address */
+MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ uint32_t
+mhd_GET_32BIT_LE_UNALIGN (const void *addr)
+{
+#ifndef mhd_GET_32BIT_LE_UNALIGNED
+  if (0 != ((uintptr_t) addr) % (mhd_UINT32_ALIGN))
+    return mhd_GET_32BIT_LE_SLOW (addr);
+  else
+#endif /* ! mhd_PUT_64BIT_LE_UNALIGNED */
+  return mhd_GET_32BIT_LE (addr);
+}
+
+
+/* mhd_PUT_64BIT_BE (addr, value64)
+ * put native-endian 64-bit value64 to addr
+ * in big-endian mode.
+ */
+/* Slow version that works with unaligned addr and with any bytes order */
+#define mhd_PUT_64BIT_BE_SLOW(addr, value64) do {                       \
+          ((uint8_t*) (addr))[7] = (uint8_t) ((uint64_t) (value64));           \
+          ((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 8);    \
+          ((uint8_t*) (addr))[5] = (uint8_t) (((uint64_t) (value64)) >> 16);   \
+          ((uint8_t*) (addr))[4] = (uint8_t) (((uint64_t) (value64)) >> 24);   \
+          ((uint8_t*) (addr))[3] = (uint8_t) (((uint64_t) (value64)) >> 32);   \
+          ((uint8_t*) (addr))[2] = (uint8_t) (((uint64_t) (value64)) >> 40);   \
+          ((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 48);   \
+          ((uint8_t*) (addr))[0] = (uint8_t) (((uint64_t) (value64)) >> 56);   \
+} while (0)
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#  define mhd_PUT_64BIT_BE(addr, value64)             \
+        ((*(uint64_t*) (addr)) = (uint64_t) (value64))
+#elif mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+#  define mhd_PUT_64BIT_BE(addr, value64)             \
+        ((*(uint64_t*) (addr)) = mhd_BYTES_SWAP64 (value64))
+#else  /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+#  define mhd_PUT_64BIT_BE(addr, value64) mhd_PUT_64BIT_BE_SLOW (addr, value64)
+/* Indicate that mhd_PUT_64BIT_BE does not need aligned pointer */
+#  define mhd_PUT_64BIT_BE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+
+/* Put result safely to unaligned address */
+MHD_static_inline_ void
+mhd_PUT_64BIT_BE_UNALIGN (void *dst, uint64_t value)
+{
+#ifndef mhd_PUT_64BIT_BE_UNALIGNED
+  if (0 != ((uintptr_t) dst) % (mhd_UINT64_ALIGN))
+    mhd_PUT_64BIT_BE_SLOW (dst, value);
+  else
+#endif /* ! mhd_PUT_64BIT_BE_UNALIGNED */
+  mhd_PUT_64BIT_BE (dst, value);
+}
+
+
+/* mhd_GET_64BIT_BE (addr)
+ * load 64-bit value located at addr in big endian mode.
+ */
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#  define mhd_GET_64BIT_BE(addr)             \
+        (*(const uint64_t*) (addr))
+#elif mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+#  define mhd_GET_64BIT_BE(addr)             \
+        mhd_BYTES_SWAP64 (*(const uint64_t*) (addr))
+#else  /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+#  define mhd_GET_64BIT_BE(addr)                          \
+        (   (((uint64_t) (((const uint8_t*) addr)[0])) << 56)   \
+            | (((uint64_t) (((const uint8_t*) addr)[1])) << 48)   \
+            | (((uint64_t) (((const uint8_t*) addr)[2])) << 40)   \
+            | (((uint64_t) (((const uint8_t*) addr)[3])) << 32)   \
+            | (((uint64_t) (((const uint8_t*) addr)[4])) << 24)   \
+            | (((uint64_t) (((const uint8_t*) addr)[5])) << 16)   \
+            | (((uint64_t) (((const uint8_t*) addr)[6])) << 8)    \
+            | ((uint64_t)  (((const uint8_t*) addr)[7])) )
+/* Indicate that mhd_GET_64BIT_BE does not need aligned pointer */
+#  define mhd_GET_64BIT_BE_ALLOW_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+
+
+/* mhd_PUT_32BIT_BE (addr, value32)
+ * put native-endian 32-bit value32 to addr
+ * in big-endian mode.
+ */
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#  define mhd_PUT_32BIT_BE(addr, value32)             \
+        ((*(uint32_t*) (addr)) = (uint32_t) (value32))
+#elif mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+#  define mhd_PUT_32BIT_BE(addr, value32)             \
+        ((*(uint32_t*) (addr)) = mhd_BYTES_SWAP32 (value32))
+#else  /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+#  define mhd_PUT_32BIT_BE(addr, value32) do {                           \
+          ((uint8_t*) (addr))[3] = (uint8_t) ((uint32_t) (value32));           \
+          ((uint8_t*) (addr))[2] = (uint8_t) (((uint32_t) (value32)) >> 8);    \
+          ((uint8_t*) (addr))[1] = (uint8_t) (((uint32_t) (value32)) >> 16);   \
+          ((uint8_t*) (addr))[0] = (uint8_t) (((uint32_t) (value32)) >> 24);   \
+} while (0)
+/* Indicate that mhd_PUT_32BIT_BE does not need aligned pointer */
+#  define mhd_PUT_32BIT_BE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+
+/* mhd_GET_32BIT_BE (addr)
+ * get big-endian 32-bit value storied at addr
+ * and return it in native-endian mode.
+ */
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#  define mhd_GET_32BIT_BE(addr)             \
+        (*(const uint32_t*) (addr))
+#elif mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN
+#  define mhd_GET_32BIT_BE(addr)             \
+        mhd_BYTES_SWAP32 (*(const uint32_t*) (addr))
+#else  /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+#  define mhd_GET_32BIT_BE(addr)                          \
+        ( (((uint32_t) (((const uint8_t*) addr)[0])) << 24)     \
+          | (((uint32_t) (((const uint8_t*) addr)[1])) << 16)   \
+          | (((uint32_t) (((const uint8_t*) addr)[2])) << 8)    \
+          | ((uint32_t) (((const uint8_t*) addr)[3])) )
+/* Indicate that mhd_GET_32BIT_BE does not need aligned pointer */
+#  define mhd_GET_32BIT_BE_UNALIGNED 1
+#endif /* mhd_BYTE_ORDER != mhd_LITTLE_ENDIAN */
+
+
+/**
+ * Rotate right 32-bit value by number of bits.
+ * bits parameter must be more than zero and must be less than 32.
+ */
+#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+  defined(__OPTIMIZE__)))
+/* Clang/C2 do not inline this function if optimizations are turned off. */
+#  ifndef __clang__
+#    pragma intrinsic(_rotr)
+#  endif /* ! __clang__ */
+#  define mhd_ROTR32(value32, bits) \
+        ((uint32_t) _rotr ((uint32_t) (value32),(bits)))
+#elif mhd_HAS_BUILTIN (__builtin_rotateright32)
+#  define mhd_ROTR32(value32, bits) \
+        ((uint32_t) __builtin_rotateright32 ((value32), (bits)))
+#else  /* ! __builtin_rotateright32 */
+MHD_static_inline_ uint32_t
+mhd_ROTR32 (uint32_t value32, int bits)
+{
+  bits %= 32;
+  if (0 == bits)
+    return value32;
+  /* Defined in form which modern compiler could optimize. */
+  return (value32 >> bits) | (value32 << (32 - bits));
+}
+
+
+#endif /* ! __builtin_rotateright32 */
+
+
+/**
+ * Rotate left 32-bit value by number of bits.
+ * bits parameter must be more than zero and must be less than 32.
+ */
+#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+  defined(__OPTIMIZE__)))
+/* Clang/C2 do not inline this function if optimizations are turned off. */
+#  ifndef __clang__
+#    pragma intrinsic(_rotl)
+#  endif /* ! __clang__ */
+#  define mhd_ROTL32(value32, bits) \
+        ((uint32_t) _rotl ((uint32_t) (value32),(bits)))
+#elif mhd_HAS_BUILTIN (__builtin_rotateleft32)
+#  define mhd_ROTL32(value32, bits) \
+        ((uint32_t) __builtin_rotateleft32 ((value32), (bits)))
+#else  /* ! __builtin_rotateleft32 */
+MHD_static_inline_ uint32_t
+mhd_ROTL32 (uint32_t value32, int bits)
+{
+  bits %= 32;
+  if (0 == bits)
+    return value32;
+  /* Defined in form which modern compiler could optimize. */
+  return (value32 << bits) | (value32 >> (32 - bits));
+}
+
+
+#endif /* ! __builtin_rotateleft32 */
+
+
+/**
+ * Rotate right 64-bit value by number of bits.
+ * bits parameter must be more than zero and must be less than 64.
+ */
+#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+  defined(__OPTIMIZE__)))
+/* Clang/C2 do not inline this function if optimisations are turned off. */
+#  ifndef __clang__
+#    pragma intrinsic(_rotr64)
+#  endif /* ! __clang__ */
+#  define mhd_ROTR64(value64, bits) \
+        ((uint64_t) _rotr64 ((uint64_t) (value64),(bits)))
+#elif mhd_HAS_BUILTIN (__builtin_rotateright64)
+#  define mhd_ROTR64(value64, bits) \
+        ((uint64_t) __builtin_rotateright64 ((value64), (bits)))
+#else  /* ! __builtin_rotateright64 */
+MHD_static_inline_ uint64_t
+mhd_ROTR64 (uint64_t value64, int bits)
+{
+  bits %= 64;
+  if (0 == bits)
+    return value64;
+  /* Defined in form which modern compiler could optimise. */
+  return (value64 >> bits) | (value64 << (64 - bits));
+}
+
+
+#endif /* ! __builtin_rotateright64 */
+
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE
+
+#endif /* ! MHD_BITHELPERS_H */

+ 173 - 0
src/mhd2/mhd_byteorder.h

@@ -0,0 +1,173 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2015-2022 Karlson2k (Evgeny Grin)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_byteorder.h
+ * @brief  macro definitions for host byte order
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_BYTEORDER_H
+#define MHD_BYTEORDER_H
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+#ifdef HAVE_ENDIAN_H
+#  include <endian.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#  include <sys/param.h>
+#endif
+
+#ifdef HAVE_MACHINE_ENDIAN_H
+#  include <machine/endian.h>
+#endif
+
+#ifdef HAVE_SYS_ENDIAN_H
+#  include <sys/endian.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_BYTEORDER_H
+#  include <sys/byteorder.h>
+#endif
+
+#ifdef HAVE_SYS_MACHINE_H
+#  include <sys/machine.h>
+#endif
+
+#ifdef HAVE_MACHINE_PARAM_H
+#  include <machine/param.h>
+#endif
+
+#ifdef HAVE_SYS_ISA_DEFS_H
+#  include <sys/isa_defs.h>
+#endif
+
+#define mhd_BIG_ENDIAN 1234
+#define mhd_LITTLE_ENDIAN 4321
+#define mhd_PDP_ENDIAN 2143
+
+#if defined(__BYTE_ORDER__)
+#  if defined(__ORDER_BIG_ENDIAN__) && \
+  __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#    define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+#  elif defined(__ORDER_LITTLE_ENDIAN__) && \
+  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#    define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+#  elif defined(__ORDER_PDP_ENDIAN__) && \
+  __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__
+#    define mhd_BYTE_ORDER mhd_PDP_ENDIAN
+#  endif /* __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__ */
+#elif defined(__BYTE_ORDER)
+#  if defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN
+#    define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+#  elif defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
+#    define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+#  elif defined(__PDP_ENDIAN) && __BYTE_ORDER == __PDP_ENDIAN
+#    define mhd_BYTE_ORDER mhd_PDP_ENDIAN
+#  endif /* __BYTE_ORDER == __PDP_ENDIAN */
+#elif defined(BYTE_ORDER)
+#  if defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN
+#    define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+#  elif defined(LITTLE_ENDIAN) && BYTE_ORDER == LITTLE_ENDIAN
+#    define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+#  elif defined(PDP_ENDIAN) && BYTE_ORDER == PDP_ENDIAN
+#    define mhd_BYTE_ORDER mhd_PDP_ENDIAN
+#  endif /* __BYTE_ORDER == _PDP_ENDIAN */
+#elif defined(_BYTE_ORDER)
+#  if defined(_BIG_ENDIAN) && _BYTE_ORDER == _BIG_ENDIAN
+#    define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+#  elif defined(_LITTLE_ENDIAN) && _BYTE_ORDER == _LITTLE_ENDIAN
+#    define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+#  elif defined(_PDP_ENDIAN) && _BYTE_ORDER == _PDP_ENDIAN
+#    define mhd_BYTE_ORDER mhd_PDP_ENDIAN
+#  endif /* _BYTE_ORDER == _PDP_ENDIAN */
+#endif /* _BYTE_ORDER */
+
+#ifndef mhd_BYTE_ORDER
+/* Byte order specification didn't detected in system headers */
+/* Try some guessing */
+
+#  if   (defined(__BIG_ENDIAN__) && ! defined(__LITTLE_ENDIAN__)) || \
+  (defined(_BIG_ENDIAN) && ! defined(_LITTLE_ENDIAN))
+/* Seems that this is a big endian platform */
+#    define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+#  elif (defined(__LITTLE_ENDIAN__) && ! defined(__BIG_ENDIAN__)) || \
+  (defined(_LITTLE_ENDIAN) && ! defined(_BIG_ENDIAN))
+/* Seems that this is a little endian platform */
+#    define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+#  elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || \
+  defined(__x86_64) || \
+  defined(_M_X64) || defined(_M_AMD64) || defined(i386) || defined(__i386) || \
+  defined(__i386__) || defined(__i486__) || defined(__i586__) || \
+  defined(__i686__) || \
+  defined(_M_IX86) || defined(_X86_) || defined(__THW_INTEL__)
+/* x86 family is little endian */
+#    define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+#  elif defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
+  defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__)
+/* Looks like this is ARM/MIPS in big endian mode */
+#    define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+#  elif defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \
+  defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
+/* Looks like this is ARM/MIPS in little endian mode */
+#    define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+#  elif defined(__m68k__) || defined(M68000) || defined(__hppa__) || \
+  defined(__hppa) || \
+  defined(__HPPA__) || defined(__370__) || defined(__THW_370__) || \
+  defined(__s390__) || defined(__s390x__) || defined(__SYSC_ZARCH__)
+/* Looks like this is a big endian platform */
+#    define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+#  elif defined(__ia64__) || defined(_IA64) || defined(__IA64__) || \
+  defined(__ia64) || \
+  defined(_M_IA64) || defined(__itanium__) || defined(__bfin__) || \
+  defined(__BFIN__) || defined(bfin) || defined(BFIN)
+/* Looks like this is a little endian platform */
+#  define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+#  elif defined(_WIN32)
+/* W32 is always little endian on all platforms, except XBOX 360 */
+#    if ! defined(_M_PPC) && ! defined(XBOX360)
+#      define mhd_BYTE_ORDER mhd_LITTLE_ENDIAN
+#    endif
+#  elif defined(WORDS_BIGENDIAN)
+/* Use byte order detected by configure */
+#    define mhd_BYTE_ORDER mhd_BIG_ENDIAN
+#  endif /* _WIN32 */
+#endif /* !mhd_BYTE_ORDER */
+
+#ifdef mhd_BYTE_ORDER
+/* Some sanity checks */
+#  if defined(WORDS_BIGENDIAN) && mhd_BYTE_ORDER != mhd_BIG_ENDIAN
+#error \
+  Configure detected big endian byte order but headers specify different byte order
+#  elif ! defined(WORDS_BIGENDIAN) && mhd_BYTE_ORDER == mhd_BIG_ENDIAN
+#error \
+  Configure did not detect big endian byte order but headers specify big endian byte order
+#  endif /* !WORDS_BIGENDIAN && mhd_BYTE_ORDER == mhd_BIG_ENDIAN */
+#endif /* mhd_BYTE_ORDER */
+
+#endif /* !MHD_BYTEORDER_H */

+ 114 - 2
src/mhd2/mhd_daemon.h

@@ -30,10 +30,17 @@
 #include "mhd_sys_options.h"
 
 #include "sys_bool_type.h"
+#include "sys_base_types.h"
 
+#include "mhd_dlinked_list.h"
+
+#include "mhd_buffer.h"
 #include "mhd_socket_type.h"
+#include "mhd_atomic_counter.h"
 
-#include "mhd_public_api.h"
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+#  include "mhd_digest_auth_data.h"
+#endif
 
 #ifdef MHD_ENABLE_HTTPS
 #  include "mhd_tls_choice.h"
@@ -51,7 +58,7 @@
 #  include <sys/epoll.h>
 #endif
 
-#include "mhd_dlinked_list.h"
+#include "mhd_public_api.h"
 
 struct DaemonOptions; /* Forward declaration */
 struct MHD_Connection; /* Forward declaration */
@@ -499,6 +506,96 @@ struct mhd_DaemonNetwork
   struct mhd_DaemonNetworkSettings cfg;
 };
 
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+
+/**
+ * Digest Auth nonce data
+ */
+struct mhd_DaemonAuthDigestNonceData
+{
+  /**
+   * The nonce value in the binary form, excluding validity tail
+   */
+  uint8_t nonce[mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE];
+
+  /**
+   * The nonce validity time
+   */
+  uint_fast32_t valid_time;
+
+  /**
+   * The largest last received 'nc' value.
+   * This 'nc' value has been already used by the client.
+   */
+  uint_fast32_t max_recvd_nc;
+
+  /**
+   * Bitmask over the previous 64 nc values (down to to nc-64).
+   * Used to allow out-of-order 'nc'.
+   * If bit in the bitmask is set to one, then this 'nc' value was already used
+   * by the client.
+   */
+  uint_fast64_t nmask;
+};
+
+/**
+ * Digest Auth daemon configuration data
+ */
+struct mhd_DaemonAuthDigestCfg
+{
+  /**
+   * The number of elements in the nonces array
+   */
+  size_t nonces_num;
+
+  /**
+   * The nonce validity time (in seconds)
+   */
+  unsigned int nonce_tmout;
+
+  /**
+   * The default maximum value of nc
+   */
+  uint_fast32_t def_max_nc;
+};
+
+/**
+ * The Digest Auth daemon's data
+ */
+struct mhd_DaemonAuthDigestData
+{
+  /**
+   * The entropy data used for Digests generation
+   */
+  struct mhd_Buffer entropy;
+
+  /**
+   * The array of generated nonce and related nc
+   */
+  struct mhd_DaemonAuthDigestNonceData *nonces;
+
+  /**
+   * Number of nonces has been generated.
+   * Used as addition for the nonce source data to ensure unique nonce value.
+   * TODO: remove and directly use random generator for nonce generation.
+   */
+  struct mhd_AtomicCounter num_gen_nonces;
+
+#ifdef MHD_USE_THREADS
+  /**
+   * The mutex to change or access the @a nonces data
+   */
+  mhd_mutex nonces_lock;
+#endif
+
+  /**
+   * Digest Auth daemon configuration data
+   */
+  struct mhd_DaemonAuthDigestCfg cfg;
+};
+
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
 #ifdef MHD_USE_THREADS
 
 /**
@@ -918,6 +1015,13 @@ struct MHD_Daemon
    */
   struct mhd_DaemonNetwork net;
 
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+  /**
+   * The Digest Auth daemon's data
+   */
+  struct mhd_DaemonAuthDigestData auth_dg;
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
 #ifdef MHD_ENABLE_HTTPS
   /**
    * The pointer to the daemon TLS data.
@@ -1035,5 +1139,13 @@ struct MHD_Daemon
 #  define mhd_D_HAS_TLS(d) (0)
 #endif
 
+/*
+ * Check whether the daemon support Digest Auth
+ */
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+#  define mhd_D_HAS_AUTH_DIGEST(d) (NULL != (d)->auth_dg.nonces)
+#else
+#  define mhd_D_HAS_AUTH_DIGEST(d) (! ! 0)
+#endif
 
 #endif /* ! MHD_DAEMON_H */

+ 20 - 5
src/mhd2/mhd_digest_auth_data.h

@@ -34,20 +34,35 @@
  */
 #define mhd_AUTH_DIGEST_SCHEME "Digest"
 
+/**
+ * The size of the random part of the nonce (in bytes)
+ */
+#define mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE     32
 /**
  * The length of the random part of the nonce (in chars)
  */
-#define mhd_AUTH_DIGEST_NONCE_RAND_LEN  64
+#define mhd_AUTH_DIGEST_NONCE_RAND_LEN \
+        (mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE * 2)
 
+/**
+ * The size of the validity time part of the nonce (in bytes)
+ */
+#define mhd_AUTH_DIGEST_NONCE_VALD_BIN_SIZE     4
 /**
  * The length of the validity time part of the nonce (in chars)
  */
-#define mhd_AUTH_DIGEST_NONCE_VALD_LEN  8
+#define mhd_AUTH_DIGEST_NONCE_VALD_LEN \
+        (mhd_AUTH_DIGEST_NONCE_VALD_BIN_SIZE * 2)
 
 /**
- * The total length of the nonce (in chars)
+ * The total size of the binary form of the nonce (in bytes)
+ */
+#define mhd_AUTH_DIGEST_NONCE_BIN_SIZE \
+        (mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE \
+         + mhd_AUTH_DIGEST_NONCE_VALD_BIN_SIZE)
+/**
+ * The total length of the nonce (in chars), without zero termination
  */
-#define mhd_AUTH_DIGEST_NONCE_LEN \
-        (mhd_AUTH_DIGEST_NONCE_RAND_LEN + mhd_AUTH_DIGEST_NONCE_VALD_LEN)
+#define mhd_AUTH_DIGEST_NONCE_LEN       (mhd_AUTH_DIGEST_NONCE_BIN_SIZE * 2)
 
 #endif /* ! MHD_DIGEST_AUTH_DATA_H */

+ 110 - 0
src/mhd2/mhd_md5.h

@@ -0,0 +1,110 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_md5.h
+ * @brief  Simple wrapper for selection of built-in/external MD5 implementation
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_MD5_H
+#define MHD_MD5_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_SUPPORT_MD5
+#error This file must be used only when MD5 is enabled
+#endif
+
+#ifndef MHD_MD5_EXTR
+#include "md5_int.h"
+#else  /* MHD_MD5_EXTR */
+#include "md5_ext.h"
+#endif /* MHD_MD5_EXTR */
+
+#ifndef mhd_MD5_DIGEST_SIZE
+/**
+ * Size of MD5 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+#  define mhd_MD5_DIGEST_SIZE (16)
+#endif /* ! mhd_MD5_DIGEST_SIZE */
+
+#ifndef MHD_MD5_EXTR
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+#  define mhd_Md5Ctx mhd_Md5CtxInt
+#else  /* MHD_MD5_EXTR */
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+#  define mhd_Md5Ctx mhd_Md5CtxExt
+#endif /* MHD_MD5_EXTR */
+
+#ifndef mhd_MD5_HAS_INIT_ONE_TIME
+/**
+ * Setup and prepare ctx for hash calculation
+ */
+#  define mhd_MD5_init_one_time(ctx) mhd_MD5_init (ctx)
+#endif /* ! mhd_MD5_HAS_INIT_ONE_TIME */
+
+#ifndef mhd_MD5_HAS_FINISH_RESET
+/**
+ * Re-use the same ctx for the new hashing after digest calculated
+ */
+#  define mhd_MD5_reset(ctx) mhd_MD5_init (ctx)
+/**
+ * Finalise MD5 calculation, return digest, reset hash calculation.
+ */
+#  define mhd_MD5_finish_reset(ctx,digest) \
+        (mhd_MD5_finish (ctx,digest), mhd_MD5_reset (ctx))
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+#  define mhd_MD5_finish_deinit(ctx,digest) \
+        (mhd_MD5_finish (ctx,digest), mhd_MD5_deinit (ctx))
+#else  /* mhd_MD5_HAS_FINISH_RESET */
+#  define mhd_MD5_reset(ctx) ((void) 0)
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+#  define mhd_MD5_finish_deinit(ctx,digest) \
+        (mhd_MD5_finish_reset (ctx,digest), mhd_MD5_deinit (ctx))
+#endif /* mhd_MD5_HAS_FINISH_RESET */
+
+#ifndef mhd_MD5_HAS_DEINIT
+#  define mhd_MD5_deinit(ctx) ((void) 0)
+#endif /* HAVE_MD5_DEINIT */
+
+
+#ifdef mhd_MD5_HAS_EXT_ERROR
+#define mhd_MD5_has_err(ctx) (0 != ((ctx)->ext_error))
+#else  /* ! mhd_MD5_HAS_EXT_ERROR */
+#define mhd_MD5_has_err(ctx) (((void) (ctx)), ! ! 0)
+#endif /* ! mhd_SHA512_256_HAS_EXT_ERROR */
+
+/* Sanity checks */
+
+#if ! defined(mhd_MD5_HAS_FINISH_RESET) && ! defined(mhd_MD5_HAS_FINISH)
+#error Required at least one of mhd_MD5_finish_reset(), mhd_MD5_finish()
+#endif /* ! mhd_MD5_HAS_FINISH_RESET && ! mhd_MD5_HAS_FINISH */
+
+#endif /* MHD_MD5_H */

+ 39 - 1
src/mhd2/mhd_request.h

@@ -320,7 +320,39 @@ union mhd_ReqAuthBasicData
 
 #endif /* MHD_SUPPORT_AUTH_BASIC */
 
-#if defined(MHD_SUPPORT_AUTH_BASIC)
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+
+struct mhd_AuthDigesReqParams; /* forward declaration */
+
+/**
+ * Request Digest Auth data
+ */
+struct mhd_ReqAuthDigestData
+{
+  /**
+   * Request Digest Auth pre-parsed data
+   */
+  struct mhd_AuthDigesReqParams *rqp;
+  /**
+   * When set to value other then #MHD_SC_OK,
+   * indicates request Digest Auth header parsing error.
+   */
+  enum MHD_StatusCode parse_result;
+  /**
+   * The information about client's Digest Auth header.
+   * NULL if not yet parsed or not found.
+   */
+  struct MHD_AuthDigestInfo *info;
+  /**
+   * The information about client's provided username.
+   * May point to the same address as @a info.
+   * NULL if not yet parsed or not found.
+   */
+  struct MHD_AuthDigestUsernameInfo *uname;
+};
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
+#if defined(MHD_SUPPORT_AUTH_BASIC) || defined(MHD_SUPPORT_AUTH_DIGEST)
 /**
  * Defined if any Authentication scheme is supported
  */
@@ -340,6 +372,12 @@ struct mhd_ReqAuthData
    */
   union mhd_ReqAuthBasicData basic;
 #endif /* MHD_SUPPORT_AUTH_BASIC */
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+  /**
+   * Request Digest Auth data
+   */
+  struct mhd_ReqAuthDigestData digest;
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
 };
 
 #endif /* mhd_SUPPORT_AUTH */

+ 10 - 0
src/mhd2/mhd_response.h

@@ -381,4 +381,14 @@ struct MHD_Response
 #endif
 };
 
+/*
+ * Check whether the response has Digest Auth headers
+ */
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+#define mhd_RESP_HAD_AUTH_DIGEST(resp) \
+        (NULL != mhd_DLINKEDL_GET_FIRST (resp, auth_d_hdrs))
+#else
+#define mhd_RESP_HAD_AUTH_DIGEST(resp) (((void) (resp)), ! ! 0)
+#endif
+
 #endif /* ! MHD_RESPONSE_H */

+ 109 - 0
src/mhd2/mhd_sha256.h

@@ -0,0 +1,109 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_sha256.h
+ * @brief  Simple wrapper for selection of built-in/external SHA-256
+ *         implementation
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA256_H
+#define MHD_SHA256_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_SUPPORT_SHA256
+#error This file must be used only when SHA-256 is enabled
+#endif
+#ifndef MHD_SHA256_EXTR
+#  include "sha256_int.h"
+#else  /* MHD_SHA256_EXTR */
+#  include "sha256_ext.h"
+#endif /* MHD_SHA256_EXTR */
+
+#ifndef mhd_SHA256_DIGEST_SIZE
+/**
+ * Size of SHA-256 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+#  define mhd_SHA256_DIGEST_SIZE (32)
+#endif /* ! mhd_SHA256_DIGEST_SIZE */
+
+#ifndef MHD_SHA256_EXTR
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+#  define mhd_Sha256Ctx mhd_Sha256CtxInt
+#else  /* MHD_SHA256_EXTR */
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+#  define mhd_Sha256Ctx mhd_Sha256CtxExt
+#endif /* MHD_SHA256_EXTR */
+
+#ifndef mhd_SHA256_HAS_INIT_ONE_TIME
+/**
+ * Setup and prepare ctx for hash calculation
+ */
+#  define mhd_SHA256_init_one_time(ctx) mhd_SHA256_init (ctx)
+#endif /* ! mhd_SHA256_HAS_INIT_ONE_TIME */
+
+#ifndef mhd_SHA256_HAS_FINISH_RESET
+/**
+ * Re-use the same ctx for the new hashing after digest calculated
+ */
+#  define mhd_SHA256_reset(ctx) mhd_SHA256_init (ctx)
+/**
+ * Finalise SHA-512/256 calculation, return digest, reset hash calculation.
+ */
+#  define mhd_SHA256_finish_reset(ctx,digest) \
+        (mhd_SHA256_finish (ctx,digest), mhd_SHA256_reset (ctx))
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+#  define mhd_SHA256_finish_deinit(ctx,digest) \
+        (mhd_SHA256_finish (ctx,digest), mhd_SHA256_deinit (ctx))
+#else  /* mhd_SHA256_HAS_FINISH_RESET */
+#  define mhd_SHA256_reset(ctx) ((void) 0)
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+#  define mhd_SHA256_finish_deinit(ctx,digest) \
+        (mhd_SHA256_finish_reset (ctx,digest), mhd_SHA256_deinit (ctx))
+#endif /* mhd_SHA256_HAS_FINISH_RESET */
+
+#ifndef mhd_SHA256_HAS_DEINIT
+#  define mhd_SHA256_deinit(ctx) ((void) 0)
+#endif /* HAVE_SHA256_DEINIT */
+
+#ifdef mhd_SHA256_HAS_EXT_ERROR
+#define mhd_SHA256_has_err(ctx) (0 != ((ctx)->ext_error))
+#else  /* ! mhd_SHA256_HAS_EXT_ERROR */
+#define mhd_SHA256_has_err(ctx) (((void) (ctx)), ! ! 0)
+#endif /* ! mhd_SHA512_256_HAS_EXT_ERROR */
+
+/* Sanity checks */
+
+#if ! defined(mhd_SHA256_HAS_FINISH_RESET) && ! defined(mhd_SHA256_HAS_FINISH)
+#error Required mhd_SHA256_finish_reset() or mhd_SHA256_finish()
+#endif /* ! mhd_SHA256_HAS_FINISH_RESET && ! mhd_SHA256_HAS_FINISH */
+
+#endif /* MHD_SHA256_H */

+ 110 - 0
src/mhd2/mhd_sha512_256.h

@@ -0,0 +1,110 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/mhd_sha512_256.h
+ * @brief  Simple wrapper for selection of built-in/external SHA-512/256
+ *         implementation
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA512_256_H
+#define MHD_SHA512_256_H 1
+
+#include "mhd_sys_options.h"
+
+#ifndef MHD_SUPPORT_SHA512_256
+#error This file must be used only when SHA-512/256 is enabled
+#endif
+#ifndef MHD_SHA512_256_EXTR
+#  include "sha512_256_int.h"
+#else  /* MHD_SHA512_256_EXTR */
+#  include "sha512_256_ext.h"
+#endif /* MHD_SHA512_256_EXTR */
+
+#ifndef mhd_SHA512_256_DIGEST_SIZE
+/**
+ * Size of SHA-512/256 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+#  define mhd_SHA512_256_DIGEST_SIZE (32)
+#endif /* ! mhd_SHA512_256_DIGEST_SIZE */
+
+#ifndef MHD_SHA512_256_EXTR
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+#  define mhd_Sha512_256Ctx mhd_Sha512_256CtxInt
+#else  /* MHD_SHA512_256_EXTR */
+/**
+ * Universal ctx type mapped for chosen implementation
+ */
+#  define mhd_Sha512_256Ctx mhd_Sha512_256CtxExt
+#endif /* MHD_SHA512_256_EXTR */
+
+#ifndef mhd_SHA512_256_HAS_INIT_ONE_TIME
+/**
+ * Setup and prepare ctx for hash calculation
+ */
+#  define mhd_SHA512_256_init_one_time(ctx) mhd_SHA512_256_init (ctx)
+#endif /* ! mhd_SHA512_256_HAS_INIT_ONE_TIME */
+
+#ifndef mhd_SHA512_256_HAS_DEINIT
+#  define mhd_SHA512_256_deinit(ctx) ((void) 0)
+#endif /* HAVE_SHA512_256_DEINIT */
+
+#ifndef mhd_SHA512_256_HAS_FINISH_RESET
+/**
+ * Re-use the same ctx for the new hashing after digest calculated
+ */
+#  define mhd_SHA512_256_reset(ctx) mhd_SHA512_256_init (ctx)
+/**
+ * Finalise hash calculation, return digest, reset hash calculation.
+ */
+#  define mhd_SHA512_256_finish_reset(ctx,digest) \
+        (mhd_SHA512_256_finish (ctx,digest), mhd_SHA512_256_reset (ctx))
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+#  define mhd_SHA512_256_finish_deinit(ctx,digest) \
+        (mhd_SHA512_256_finish (ctx,digest), mhd_SHA512_256_deinit (ctx))
+#else  /* mhd_SHA512_256_HAS_FINISH_RESET */
+#  define mhd_SHA512_256_reset(ctx) ((void) 0)
+/**
+ * Finalise hash calculation, return digest, de-initialise hash calculation.
+ */
+#  define mhd_SHA512_256_finish_deinit(ctx,digest) \
+        (mhd_SHA512_256_finish_reset (ctx,digest), mhd_SHA512_256_deinit (ctx))
+#endif /* mhd_SHA512_256_HAS_FINISH_RESET */
+
+/* Sanity checks */
+
+#if ! defined(mhd_SHA512_256_HAS_FINISH_RESET) && \
+  ! defined(mhd_SHA512_256_HAS_FINISH)
+#error Required mhd_SHA512_256_finish_reset() or mhd_SHA512_256_finish()
+#endif /* ! mhd_SHA512_256_HAS_FINISH_RESET && ! mhd_SHA512_256_HAS_FINISH */
+
+#ifdef mhd_SHA512_256_HAS_EXT_ERROR
+#define mhd_SHA512_256_has_err(ctx) (0 != ((ctx)->ext_error))
+#else  /* ! mhd_SHA512_256_HAS_EXT_ERROR */
+#define mhd_SHA512_256_has_err(ctx) (((void) (ctx)), ! ! 0)
+#endif /* ! mhd_SHA512_256_HAS_EXT_ERROR */
+
+#endif /* MHD_SHA512_256_H */

+ 2 - 2
src/mhd2/mhd_str.c

@@ -2156,7 +2156,7 @@ base64_char_to_value_ (uint8_t c)
 #endif /* MHD_BASE64_FUNC_VERSION == 1 */
 
 
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
 
 
 MHD_INTERNAL size_t
@@ -2328,7 +2328,7 @@ mhd_base64_to_bin_n (const char *base64,
 }
 
 
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE
 
 
 #undef mhd_base64_map_type

+ 9 - 0
src/mhd2/request_auth_get.c

@@ -52,6 +52,9 @@ mhd_request_get_auth_header_value (struct MHD_Request *restrict request,
   static const struct MHD_String hdr_name = mhd_MSTR_INIT ("Authorization");
 #ifdef MHD_SUPPORT_AUTH_BASIC
   static const struct MHD_String prefix_basic = mhd_MSTR_INIT ("Basic");
+#endif
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+  static const struct MHD_String prefix_digest = mhd_MSTR_INIT ("Digest");
 #endif
   const int strict_lvl =
     mhd_CNTNR_PTR (request, \
@@ -73,6 +76,12 @@ mhd_request_get_auth_header_value (struct MHD_Request *restrict request,
     prefix_len = prefix_basic.len;
     break;
 #endif
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+  case mhd_AUTH_HDR_DIGEST:
+    prefix_str = prefix_digest.cstr;
+    prefix_len = prefix_digest.len;
+    break;
+#endif
 #ifdef MHD_ENUMS_NEED_TRAILING_VALUE
   case mhd_AUTH_HDR_KIND_SENTINEL:
 #endif

+ 3 - 0
src/mhd2/request_auth_get.h

@@ -46,6 +46,9 @@ enum MHD_FIXED_ENUM_ mhd_AuthHeaderKind
 #ifdef MHD_SUPPORT_AUTH_BASIC
   mhd_AUTH_HDR_BASIC,
 #endif
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+  mhd_AUTH_HDR_DIGEST,
+#endif
 #ifdef MHD_ENUMS_NEED_TRAILING_VALUE
   mhd_AUTH_HDR_KIND_SENTINEL
 #endif

+ 24 - 3
src/mhd2/request_get_info.c

@@ -39,6 +39,9 @@
 #ifdef MHD_SUPPORT_AUTH_BASIC
 #  include "auth_basic.h"
 #endif
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+#  include "auth_digest.h"
+#endif
 
 #include "mhd_public_api.h"
 
@@ -142,9 +145,27 @@ MHD_request_get_info_dynamic_sz (
   case MHD_REQUEST_INFO_DYNAMIC_APP_CONTEXT:
     mhd_assert (0 && "Not implemented yet");
     break;
-  case MHD_REQUEST_INFO_DYNAMIC_DAUTH_USERNAME_INFO:
-  case MHD_REQUEST_INFO_DYNAMIC_DAUTH_REQ_INFO:
-    mhd_assert (0 && "Not implemented yet");
+  case MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_USERNAME:
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+    if (sizeof(return_value->v_auth_basic_creds) > return_value_size)
+      return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+    return mhd_request_get_auth_digest_username (request,
+                                                 &(return_value->
+                                                   v_auth_digest_uname));
+#else  /* ! MHD_SUPPORT_AUTH_DIGEST */
+    return MHD_SC_FEATURE_DISABLED;
+#endif /* ! MHD_SUPPORT_AUTH_DIGEST */
+    break;
+  case MHD_REQUEST_INFO_DYNAMIC_AUTH_DIGEST_INFO:
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+    if (sizeof(return_value->v_auth_basic_creds) > return_value_size)
+      return MHD_SC_INFO_GET_BUFF_TOO_SMALL;
+    return mhd_request_get_auth_digest_info (request,
+                                             &(return_value->
+                                               v_auth_digest_info));
+#else  /* ! MHD_SUPPORT_AUTH_DIGEST */
+    return MHD_SC_FEATURE_DISABLED;
+#endif /* ! MHD_SUPPORT_AUTH_DIGEST */
     break;
   case MHD_REQUEST_INFO_DYNAMIC_AUTH_BASIC_CREDS:
 #ifdef MHD_SUPPORT_AUTH_BASIC

+ 12 - 10
src/mhd2/response_auth_digest.c

@@ -111,9 +111,9 @@ response_add_auth_digest_challenge_alg (
 #endif
   static const struct MHD_String nonce_str =
     mhd_MSTR_INIT (", nonce=\"" \
-                   "00000000000000000000000000000000" \
-                   "00000000000000000000000000000000" \
-                   "00000000" \
+                   "################################" \
+                   "################################" \
+                   "########" \
                    "\"");
   static size_t nonce_off = 9; /* Position of nonce value in the nonce_str */
   static const struct MHD_String opaque_pref =
@@ -147,10 +147,10 @@ response_add_auth_digest_challenge_alg (
   size_t pos;
   size_t elm_len;
 
-  mhd_assert ('0' == nonce_str.cstr[nonce_off]);
-  mhd_assert ('0' == nonce_str.cstr[nonce_off + mhd_AUTH_DIGEST_NONCE_LEN]);
+  mhd_assert ('#' == nonce_str.cstr[nonce_off]);
+  mhd_assert ('#' == nonce_str.cstr[nonce_off + mhd_AUTH_DIGEST_NONCE_LEN - 1]);
   mhd_assert ('"' == nonce_str.cstr[nonce_off - 1]);
-  mhd_assert ('"' == nonce_str.cstr[nonce_off + mhd_AUTH_DIGEST_NONCE_LEN + 1]);
+  mhd_assert ('"' == nonce_str.cstr[nonce_off + mhd_AUTH_DIGEST_NONCE_LEN]);
 
 #ifdef MHD_SUPPORT_MD5
   if (MHD_DIGEST_AUTH_ALGO_MD5 == algo)
@@ -326,7 +326,7 @@ response_add_auth_digest_challenge_alg (
     void *new_ptr;
     new_ptr = realloc (new_hdr,
                        sizeof(struct mhd_RespAuthDigestHeader)
-                       + pos);
+                       + pos + 1);
     /* Just use the old pointer if realloc() failed */
     if (NULL != new_ptr)
       new_hdr = (struct mhd_RespAuthDigestHeader *) new_ptr;
@@ -339,6 +339,8 @@ response_add_auth_digest_challenge_alg (
                       MHD_HTTP_HEADER_WWW_AUTHENTICATE ": ", \
                       mhd_SSTR_LEN (MHD_HTTP_HEADER_WWW_AUTHENTICATE ": ")));
   mhd_assert (0 == new_hdr->hdr.cstr[new_hdr->hdr.len]);
+  mhd_assert ('\r' == new_hdr->hdr.cstr[new_hdr->hdr.len - 2]);
+  mhd_assert ('\n' == new_hdr->hdr.cstr[new_hdr->hdr.len - 1]);
 
   mhd_DLINKEDL_INIT_LINKS (new_hdr, auth_d_hdrs);
   mhd_DLINKEDL_INS_LAST (response, new_hdr, auth_d_hdrs);
@@ -413,7 +415,7 @@ response_add_auth_digest_challenge_int (struct MHD_Response *restrict response,
 
 #ifdef MHD_SUPPORT_MD5
   if ((MHD_SC_OK == res) &&
-      (0 == (MHD_DIGEST_BASE_ALGO_MD5 & ((unsigned int) malgo))))
+      (0 != (MHD_DIGEST_BASE_ALGO_MD5 & ((unsigned int) malgo))))
     res = response_add_auth_digest_challenge_alg (response,
                                                   &rlm,
                                                   &opq,
@@ -426,7 +428,7 @@ response_add_auth_digest_challenge_int (struct MHD_Response *restrict response,
 #endif
 #ifdef MHD_SUPPORT_SHA256
   if ((MHD_SC_OK == res) &&
-      (0 == (MHD_DIGEST_BASE_ALGO_SHA256 & ((unsigned int) malgo))))
+      (0 != (MHD_DIGEST_BASE_ALGO_SHA256 & ((unsigned int) malgo))))
     res = response_add_auth_digest_challenge_alg (response,
                                                   &rlm,
                                                   &opq,
@@ -439,7 +441,7 @@ response_add_auth_digest_challenge_int (struct MHD_Response *restrict response,
 #endif
 #ifdef MHD_SUPPORT_SHA512_256
   if ((MHD_SC_OK == res) &&
-      (0 == (MHD_DIGEST_BASE_ALGO_SHA256 & ((unsigned int) malgo))))
+      (0 != (MHD_DIGEST_BASE_ALGO_SHA256 & ((unsigned int) malgo))))
     res = response_add_auth_digest_challenge_alg (
       response,
       &rlm,

+ 9 - 9
src/microhttpd/sha256_ext.c → src/mhd2/sha256_ext.c

@@ -36,7 +36,7 @@
  * @param ctx the calculation context
  */
 void
-MHD_SHA256_init_one_time (struct Sha256CtxExt *ctx)
+mhd_SHA256_init_one_time (struct mhd_Sha256CtxExt *ctx)
 {
   ctx->handle = NULL;
   ctx->ext_error = gnutls_hash_init (&ctx->handle, GNUTLS_DIG_SHA256);
@@ -64,12 +64,12 @@ MHD_SHA256_init_one_time (struct Sha256CtxExt *ctx)
  * @param length number of bytes in @a data
  */
 void
-MHD_SHA256_update (struct Sha256CtxExt *ctx,
-                   const uint8_t *data,
-                   size_t length)
+mhd_SHA256_update (struct mhd_Sha256CtxExt *ctx,
+                   size_t size,
+                   const uint8_t *data)
 {
   if (0 == ctx->ext_error)
-    ctx->ext_error = gnutls_hash (ctx->handle, data, length);
+    ctx->ext_error = gnutls_hash (ctx->handle, data, size);
 }
 
 
@@ -77,11 +77,11 @@ MHD_SHA256_update (struct Sha256CtxExt *ctx,
  * Finalise SHA-256 calculation, return digest, reset hash calculation.
  *
  * @param ctx the calculation context
- * @param[out] digest set to the hash, must be #SHA256_DIGEST_SIZE bytes
+ * @param[out] digest set to the hash, must be #mhd_SHA256_DIGEST_SIZE bytes
  */
 void
-MHD_SHA256_finish_reset (struct Sha256CtxExt *ctx,
-                         uint8_t digest[SHA256_DIGEST_SIZE])
+mhd_SHA256_finish_reset (struct mhd_Sha256CtxExt *ctx,
+                         uint8_t digest[mhd_SHA256_DIGEST_SIZE])
 {
   if (0 == ctx->ext_error)
     gnutls_hash_output (ctx->handle, digest);
@@ -94,7 +94,7 @@ MHD_SHA256_finish_reset (struct Sha256CtxExt *ctx,
  * @param ctx the calculation context
  */
 void
-MHD_SHA256_deinit (struct Sha256CtxExt *ctx)
+mhd_SHA256_deinit (struct mhd_Sha256CtxExt *ctx)
 {
   if (NULL != ctx->handle)
     gnutls_hash_deinit (ctx->handle, NULL);

+ 19 - 19
src/microhttpd/sha256_ext.h → src/mhd2/sha256_ext.h

@@ -36,29 +36,29 @@
  * Size of SHA-256 resulting digest in bytes
  * This is the final digest size, not intermediate hash.
  */
-#define SHA256_DIGEST_SIZE (32)
+#define mhd_SHA256_DIGEST_SIZE (32)
 
 /* Actual declaration is in GnuTLS lib header */
 struct hash_hd_st;
 
 /**
- * Indicates that struct Sha256CtxExt has 'ext_error'
+ * Indicates that struct mhd_Sha256CtxExt has 'ext_error'
  */
-#define MHD_SHA256_HAS_EXT_ERROR 1
+#define mhd_SHA256_HAS_EXT_ERROR 1
 
 /**
  * SHA-256 calculation context
  */
-struct Sha256CtxExt
+struct mhd_Sha256CtxExt
 {
   struct hash_hd_st *handle; /**< Hash calculation handle */
   int ext_error; /**< Non-zero if external error occurs during init or hashing */
 };
 
 /**
- * Indicates that MHD_SHA256_init_one_time() function is present.
+ * Indicates that mhd_SHA256_init_one_time() function is present.
  */
-#define MHD_SHA256_HAS_INIT_ONE_TIME 1
+#define mhd_SHA256_HAS_INIT_ONE_TIME 1
 
 /**
  * Initialise structure for SHA-256 calculation, allocate resources.
@@ -68,41 +68,41 @@ struct Sha256CtxExt
  * @param ctx the calculation context
  */
 void
-MHD_SHA256_init_one_time (struct Sha256CtxExt *ctx);
+mhd_SHA256_init_one_time (struct mhd_Sha256CtxExt *ctx);
 
 
 /**
  * SHA-256 process portion of bytes.
  *
  * @param ctx the calculation context
+ * @param size number of bytes in @a data
  * @param data bytes to add to hash
- * @param length number of bytes in @a data
  */
 void
-MHD_SHA256_update (struct Sha256CtxExt *ctx,
-                   const uint8_t *data,
-                   size_t length);
+mhd_SHA256_update (struct mhd_Sha256CtxExt *ctx,
+                   size_t size,
+                   const uint8_t *data);
 
 
 /**
- * Indicates that MHD_SHA256_finish_reset() function is available
+ * Indicates that mhd_SHA256_finish_reset() function is available
  */
-#define MHD_SHA256_HAS_FINISH_RESET 1
+#define mhd_SHA256_HAS_FINISH_RESET 1
 
 /**
  * Finalise SHA-256 calculation, return digest, reset hash calculation.
  *
  * @param ctx the calculation context
- * @param[out] digest set to the hash, must be #SHA256_DIGEST_SIZE bytes
+ * @param[out] digest set to the hash, must be #mhd_SHA256_DIGEST_SIZE bytes
  */
 void
-MHD_SHA256_finish_reset (struct Sha256CtxExt *ctx,
-                         uint8_t digest[SHA256_DIGEST_SIZE]);
+mhd_SHA256_finish_reset (struct mhd_Sha256CtxExt *ctx,
+                         uint8_t digest[mhd_SHA256_DIGEST_SIZE]);
 
 /**
- * Indicates that MHD_SHA256_deinit() function is present
+ * Indicates that mhd_SHA256_deinit() function is present
  */
-#define MHD_SHA256_HAS_DEINIT 1
+#define mhd_SHA256_HAS_DEINIT 1
 
 /**
  * Free allocated resources.
@@ -110,6 +110,6 @@ MHD_SHA256_finish_reset (struct Sha256CtxExt *ctx,
  * @param ctx the calculation context
  */
 void
-MHD_SHA256_deinit (struct Sha256CtxExt *ctx);
+mhd_SHA256_deinit (struct mhd_Sha256CtxExt *ctx);
 
 #endif /* MHD_SHA256_EXT_H */

+ 139 - 148
src/microhttpd/sha256.c → src/mhd2/sha256_int.c

@@ -1,44 +1,41 @@
 /*
-     This file is part of libmicrohttpd
-     Copyright (C) 2019-2023 Evgeny Grin (Karlson2k)
-
-     libmicrohttpd is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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, see <http://www.gnu.org/licenses/>.
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2019-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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/sha256.c
+ * @file src/mhd2/sha256.c
  * @brief  Calculation of SHA-256 digest as defined in FIPS PUB 180-4 (2015)
  * @author Karlson2k (Evgeny Grin)
  */
 
-#include "sha256.h"
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
 
 #include <string.h>
-#ifdef HAVE_MEMORY_H
-#include <memory.h>
-#endif /* HAVE_MEMORY_H */
 #include "mhd_bithelpers.h"
 #include "mhd_assert.h"
 
-/**
- * Initialise structure for SHA256 calculation.
- *
- * @param ctx must be a `struct Sha256Ctx *`
- */
-void
-MHD_SHA256_init (struct Sha256Ctx *ctx)
+#include "sha256_int.h"
+
+MHD_INTERNAL void MHD_FN_PAR_NONNULL_ALL_
+mhd_SHA256_init (struct mhd_Sha256CtxInt *ctx)
 {
   /* Initial hash values, see FIPS PUB 180-4 paragraph 5.3.3 */
   /* First thirty-two bits of the fractional parts of the square
@@ -58,17 +55,11 @@ MHD_SHA256_init (struct Sha256Ctx *ctx)
 }
 
 
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
 
-/**
- * Base of SHA-256 transformation.
- * Gets full 64 bytes block of data and updates hash values;
- * @param H     hash values
- * @param data  data, must be exactly 64 bytes long
- */
-static void
-sha256_transform (uint32_t H[SHA256_DIGEST_SIZE_WORDS],
-                  const void *data)
+static MHD_FN_PAR_NONNULL_ALL_ void
+sha256_transform (uint32_t H[mhd_SHA256_DIGEST_SIZE_WORDS],
+                  const void *restrict data)
 {
   /* Working variables,
      see FIPS PUB 180-4 paragraph 6.2. */
@@ -85,17 +76,17 @@ sha256_transform (uint32_t H[SHA256_DIGEST_SIZE_WORDS],
      See FIPS PUB 180-4 paragraphs 5.2.1, 6.2. */
   uint32_t W[16];
 
-#ifndef _MHD_GET_32BIT_BE_UNALIGNED
-  if (0 != (((uintptr_t) data) % _MHD_UINT32_ALIGN))
+#ifndef mhd_GET_32BIT_BE_UNALIGNED
+  if (0 != (((uintptr_t) data) % mhd_UINT32_ALIGN))
   {
     /* Copy the unaligned input data to the aligned buffer */
-    memcpy (W, data, SHA256_BLOCK_SIZE);
+    memcpy (W, data, mhd_SHA256_BLOCK_SIZE);
     /* The W[] buffer itself will be used as the source of the data,
      * but data will be reloaded in correct bytes order during
      * the next steps */
     data = (const void *) W;
   }
-#endif /* _MHD_GET_32BIT_BE_UNALIGNED */
+#endif /* mhd_GET_32BIT_BE_UNALIGNED */
 
   /* 'Ch' and 'Maj' macro functions are defined with
      widely-used optimization.
@@ -108,13 +99,13 @@ sha256_transform (uint32_t H[SHA256_DIGEST_SIZE_WORDS],
 
   /* Four 'Sigma' macro functions.
      See FIPS PUB 180-4 formulae 4.4, 4.5, 4.6, 4.7. */
-#define SIG0(x)  (_MHD_ROTR32 ((x), 2) ^ _MHD_ROTR32 ((x), 13) ^ \
-                  _MHD_ROTR32 ((x), 22) )
-#define SIG1(x)  (_MHD_ROTR32 ((x), 6) ^ _MHD_ROTR32 ((x), 11) ^ \
-                  _MHD_ROTR32 ((x), 25) )
-#define sig0(x)  (_MHD_ROTR32 ((x), 7) ^ _MHD_ROTR32 ((x), 18) ^ \
+#define SIG0(x)  (mhd_ROTR32 ((x), 2) ^ mhd_ROTR32 ((x), 13) ^ \
+                  mhd_ROTR32 ((x), 22) )
+#define SIG1(x)  (mhd_ROTR32 ((x), 6) ^ mhd_ROTR32 ((x), 11) ^ \
+                  mhd_ROTR32 ((x), 25) )
+#define sig0(x)  (mhd_ROTR32 ((x), 7) ^ mhd_ROTR32 ((x), 18) ^ \
                   ((x) >> 3) )
-#define sig1(x)  (_MHD_ROTR32 ((x), 17) ^ _MHD_ROTR32 ((x),19) ^ \
+#define sig1(x)  (mhd_ROTR32 ((x), 17) ^ mhd_ROTR32 ((x),19) ^ \
                   ((x) >> 10) )
 
   /* One step of SHA-256 computation,
@@ -125,8 +116,8 @@ sha256_transform (uint32_t H[SHA256_DIGEST_SIZE_WORDS],
    * Note: 'wt' must be used exactly one time in this macro as it change other data as well
            every time when used. */
 #define SHA2STEP32(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do {                  \
-    (vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt));  \
-    (vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0)
+          (vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt));  \
+          (vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0)
 
   /* Get value of W(t) from input data buffer,
      See FIPS PUB 180-4 paragraph 6.2.
@@ -135,8 +126,8 @@ sha256_transform (uint32_t H[SHA256_DIGEST_SIZE_WORDS],
   /* Use cast to (const void*) to mute compiler alignment warning,
    * data was already aligned in previous step */
 #define GET_W_FROM_DATA(buf,t) \
-  _MHD_GET_32BIT_BE ((const void*)(((const uint8_t*) (buf)) + \
-                                   (t) * SHA256_BYTES_IN_WORD))
+        mhd_GET_32BIT_BE ((const void*) (((const uint8_t*) (buf)) + \
+                                         (t) * mhd_SHA256_BYTES_IN_WORD))
 
   /* 'W' generation and assignment for 16 <= t <= 63.
      See FIPS PUB 180-4 paragraph 6.2.2.
@@ -157,7 +148,7 @@ sha256_transform (uint32_t H[SHA256_DIGEST_SIZE_WORDS],
              SHA2STEP32(h, a, b, c, d, e, f, g, K[1], data[1]);
            so current 'vD' will be used as 'vE' on next step,
            current 'vH' will be used as 'vA' on next step. */
-#if _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
   if ((const void *) W == data)
   {
     /* The input data is already in the cyclic data buffer W[] in correct bytes
@@ -180,7 +171,7 @@ sha256_transform (uint32_t H[SHA256_DIGEST_SIZE_WORDS],
     SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0xc19bf174), W[15]);
   }
   else /* Combined with the next 'if' */
-#endif /* _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN */
+#endif /* mhd_BYTE_ORDER == mhd_BIG_ENDIAN */
   if (1)
   {
     /* During first 16 steps, before making any calculations on each step,
@@ -355,16 +346,18 @@ sha256_transform (uint32_t H[SHA256_DIGEST_SIZE_WORDS],
      * Note: this version of macro reassign all working variable on
              each step. */
 #define SHA2STEP32RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do {              \
-    uint32_t tmp_h_ = (vH);                                           \
-    SHA2STEP32((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt));  \
-    (vH) = (vG);                                                      \
-    (vG) = (vF);                                                      \
-    (vF) = (vE);                                                      \
-    (vE) = (vD);                                                      \
-    (vD) = (vC);                                                      \
-    (vC) = (vB);                                                      \
-    (vB) = (vA);                                                      \
-    (vA) = tmp_h_;  } while (0)
+          uint32_t tmp_h_ = (vH);                                           \
+          SHA2STEP32 ((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt));  \
+          (vH) = (vG);                                                      \
+          (vG) = (vF);                                                      \
+          (vF) = (vE);                                                      \
+          (vE) = (vD);                                                      \
+          (vD) = (vC);                                                      \
+          (vC) = (vB);                                                      \
+          (vB) = (vA);                                                      \
+          (vA) = tmp_h_; \
+} \
+        while (0)
 
     /* During first 16 steps, before making any calculations on each step,
        the W element is read from input data buffer as big-endian value and
@@ -385,7 +378,6 @@ sha256_transform (uint32_t H[SHA256_DIGEST_SIZE_WORDS],
   }
 #endif /* ! MHD_FAVOR_SMALL_CODE */
 
-
   /* Compute intermediate hash.
      See FIPS PUB 180-4 paragraph 6.2.2 step 4. */
   H[0] += a;
@@ -399,60 +391,54 @@ sha256_transform (uint32_t H[SHA256_DIGEST_SIZE_WORDS],
 }
 
 
-/**
- * Process portion of bytes.
- *
- * @param ctx_ must be a `struct Sha256Ctx *`
- * @param data bytes to add to hash
- * @param length number of bytes in @a data
- */
-void
-MHD_SHA256_update (struct Sha256Ctx *ctx,
-                   const uint8_t *data,
-                   size_t length)
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) void
+mhd_SHA256_update (struct mhd_Sha256CtxInt *restrict ctx,
+                   size_t size,
+                   const uint8_t *restrict data)
 {
   unsigned bytes_have; /**< Number of bytes in buffer */
 
-  mhd_assert ((data != NULL) || (length == 0));
+  mhd_assert ((data != NULL) || (size == 0));
 
 #ifndef MHD_FAVOR_SMALL_CODE
-  if (0 == length)
+  if (0 == size)
     return; /* Shortcut, do nothing */
 #endif /* MHD_FAVOR_SMALL_CODE */
 
-  /* Note: (count & (SHA256_BLOCK_SIZE-1))
-           equals (count % SHA256_BLOCK_SIZE) for this block size. */
-  bytes_have = (unsigned) (ctx->count & (SHA256_BLOCK_SIZE - 1));
-  ctx->count += length;
+  /* Note: (count & (mhd_SHA256_BLOCK_SIZE-1))
+           equals (count % mhd_SHA256_BLOCK_SIZE) for this block size. */
+  bytes_have = (unsigned) (ctx->count & (mhd_SHA256_BLOCK_SIZE - 1));
+  ctx->count += size;
 
   if (0 != bytes_have)
   {
-    unsigned bytes_left = SHA256_BLOCK_SIZE - bytes_have;
-    if (length >= bytes_left)
+    unsigned bytes_left = mhd_SHA256_BLOCK_SIZE - bytes_have;
+    if (size >= bytes_left)
     {     /* Combine new data with data in the buffer and
              process full block. */
       memcpy (((uint8_t *) ctx->buffer) + bytes_have,
               data,
               bytes_left);
       data += bytes_left;
-      length -= bytes_left;
+      size -= bytes_left;
       sha256_transform (ctx->H, ctx->buffer);
       bytes_have = 0;
     }
   }
 
-  while (SHA256_BLOCK_SIZE <= length)
+  while (mhd_SHA256_BLOCK_SIZE <= size)
   {   /* Process any full blocks of new data directly,
          without copying to the buffer. */
     sha256_transform (ctx->H, data);
-    data += SHA256_BLOCK_SIZE;
-    length -= SHA256_BLOCK_SIZE;
+    data += mhd_SHA256_BLOCK_SIZE;
+    size -= mhd_SHA256_BLOCK_SIZE;
   }
 
-  if (0 != length)
+  if (0 != size)
   {   /* Copy incomplete block of new data (if any)
          to the buffer. */
-    memcpy (((uint8_t *) ctx->buffer) + bytes_have, data, length);
+    memcpy (((uint8_t *) ctx->buffer) + bytes_have, data, size);
   }
 }
 
@@ -463,23 +449,17 @@ MHD_SHA256_update (struct Sha256Ctx *ctx,
  */
 #define SHA256_SIZE_OF_LEN_ADD (64 / 8)
 
-/**
- * Finalise SHA256 calculation, return digest.
- *
- * @param ctx_ must be a `struct Sha256Ctx *`
- * @param[out] digest set to the hash, must be #SHA256_DIGEST_SIZE bytes
- */
-void
-MHD_SHA256_finish (struct Sha256Ctx *ctx,
-                   uint8_t digest[SHA256_DIGEST_SIZE])
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_SHA256_finish (struct mhd_Sha256CtxInt *restrict ctx,
+                   uint8_t digest[mhd_SHA256_DIGEST_SIZE])
 {
   uint64_t num_bits;   /**< Number of processed bits */
   unsigned bytes_have; /**< Number of bytes in buffer */
 
   num_bits = ctx->count << 3;
-  /* Note: (count & (SHA256_BLOCK_SIZE-1))
-           equal (count % SHA256_BLOCK_SIZE) for this block size. */
-  bytes_have = (unsigned) (ctx->count & (SHA256_BLOCK_SIZE - 1));
+  /* Note: (count & (mhd_SHA256_BLOCK_SIZE-1))
+           equal (count % mhd_SHA256_BLOCK_SIZE) for this block size. */
+  bytes_have = (unsigned) (ctx->count & (mhd_SHA256_BLOCK_SIZE - 1));
 
   /* Input data must be padded with a single bit "1", then with zeros and
      the finally the length of data in bits must be added as the final bytes
@@ -493,12 +473,12 @@ MHD_SHA256_finish (struct Sha256Ctx *ctx,
      processed immediately). */
   ((uint8_t *) ctx->buffer)[bytes_have++] = 0x80;
 
-  if (SHA256_BLOCK_SIZE - bytes_have < SHA256_SIZE_OF_LEN_ADD)
+  if (mhd_SHA256_BLOCK_SIZE - bytes_have < SHA256_SIZE_OF_LEN_ADD)
   {   /* No space in current block to put total length of message.
          Pad current block with zeros and process it. */
-    if (bytes_have < SHA256_BLOCK_SIZE)
+    if (bytes_have < mhd_SHA256_BLOCK_SIZE)
       memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
-              SHA256_BLOCK_SIZE - bytes_have);
+              mhd_SHA256_BLOCK_SIZE - bytes_have);
     /* Process full block. */
     sha256_transform (ctx->H, ctx->buffer);
     /* Start new block. */
@@ -507,59 +487,70 @@ MHD_SHA256_finish (struct Sha256Ctx *ctx,
 
   /* Pad the rest of the buffer with zeros. */
   memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
-          SHA256_BLOCK_SIZE - SHA256_SIZE_OF_LEN_ADD - bytes_have);
+          mhd_SHA256_BLOCK_SIZE - SHA256_SIZE_OF_LEN_ADD - bytes_have);
   /* Put the number of bits in processed message as big-endian value. */
-  _MHD_PUT_64BIT_BE_SAFE (ctx->buffer + SHA256_BLOCK_SIZE_WORDS - 2, num_bits);
+  mhd_PUT_64BIT_BE_UNALIGN (ctx->buffer + mhd_SHA256_BLOCK_SIZE_WORDS - 2,
+                            num_bits);
   /* Process full final block. */
   sha256_transform (ctx->H, ctx->buffer);
 
   /* Put final hash/digest in BE mode */
-#ifndef _MHD_PUT_32BIT_BE_UNALIGNED
-  if (1
-#ifndef MHD_FAVOR_SMALL_CODE
-      && (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN)
-#endif /* MHD_FAVOR_SMALL_CODE */
-      )
-  {
-    /* If storing of the final result requires aligned address and
-       the destination address is not aligned or compact code is used,
-       store the final digest in aligned temporary buffer first, then
-       copy it to the destination. */
-    uint32_t alig_dgst[SHA256_DIGEST_SIZE_WORDS];
-    _MHD_PUT_32BIT_BE (alig_dgst + 0, ctx->H[0]);
-    _MHD_PUT_32BIT_BE (alig_dgst + 1, ctx->H[1]);
-    _MHD_PUT_32BIT_BE (alig_dgst + 2, ctx->H[2]);
-    _MHD_PUT_32BIT_BE (alig_dgst + 3, ctx->H[3]);
-    _MHD_PUT_32BIT_BE (alig_dgst + 4, ctx->H[4]);
-    _MHD_PUT_32BIT_BE (alig_dgst + 5, ctx->H[5]);
-    _MHD_PUT_32BIT_BE (alig_dgst + 6, ctx->H[6]);
-    _MHD_PUT_32BIT_BE (alig_dgst + 7, ctx->H[7]);
-    /* Copy result to unaligned destination address */
-    memcpy (digest, alig_dgst, SHA256_DIGEST_SIZE);
-  }
-#ifndef MHD_FAVOR_SMALL_CODE
-  else /* Combined with the next 'if' */
-#endif /* MHD_FAVOR_SMALL_CODE */
-#endif /* ! _MHD_PUT_32BIT_BE_UNALIGNED */
-#if ! defined(MHD_FAVOR_SMALL_CODE) || defined(_MHD_PUT_32BIT_BE_UNALIGNED)
   if (1)
   {
-    /* Use cast to (void*) here to mute compiler alignment warnings.
-     * Compilers are not smart enough to see that alignment has been checked. */
-    _MHD_PUT_32BIT_BE ((void *) (digest + 0 * SHA256_BYTES_IN_WORD), ctx->H[0]);
-    _MHD_PUT_32BIT_BE ((void *) (digest + 1 * SHA256_BYTES_IN_WORD), ctx->H[1]);
-    _MHD_PUT_32BIT_BE ((void *) (digest + 2 * SHA256_BYTES_IN_WORD), ctx->H[2]);
-    _MHD_PUT_32BIT_BE ((void *) (digest + 3 * SHA256_BYTES_IN_WORD), ctx->H[3]);
-    _MHD_PUT_32BIT_BE ((void *) (digest + 4 * SHA256_BYTES_IN_WORD), ctx->H[4]);
-    _MHD_PUT_32BIT_BE ((void *) (digest + 5 * SHA256_BYTES_IN_WORD), ctx->H[5]);
-    _MHD_PUT_32BIT_BE ((void *) (digest + 6 * SHA256_BYTES_IN_WORD), ctx->H[6]);
-    _MHD_PUT_32BIT_BE ((void *) (digest + 7 * SHA256_BYTES_IN_WORD), ctx->H[7]);
+    bool use_tmp_buf_to_align_result;
+
+#if defined(mhd_PUT_32BIT_BE_UNALIGNED)
+    use_tmp_buf_to_align_result = false;
+#elif defined (MHD_FAVOR_SMALL_CODE)
+    use_tmp_buf_to_align_result = true; /* smaller code: eliminated branch below */
+#else
+    use_tmp_buf_to_align_result =
+      (0 != ((uintptr_t) digest) % mhd_UINT32_ALIGN);
+#endif
+    if (use_tmp_buf_to_align_result)
+    {
+      /* If storing of the final result requires aligned address and
+         the destination address is not aligned or compact code is used,
+         store the final digest in aligned temporary buffer first, then
+         copy it to the destination. */
+      uint32_t alig_dgst[mhd_SHA256_DIGEST_SIZE_WORDS];
+      mhd_PUT_32BIT_BE (alig_dgst + 0, ctx->H[0]);
+      mhd_PUT_32BIT_BE (alig_dgst + 1, ctx->H[1]);
+      mhd_PUT_32BIT_BE (alig_dgst + 2, ctx->H[2]);
+      mhd_PUT_32BIT_BE (alig_dgst + 3, ctx->H[3]);
+      mhd_PUT_32BIT_BE (alig_dgst + 4, ctx->H[4]);
+      mhd_PUT_32BIT_BE (alig_dgst + 5, ctx->H[5]);
+      mhd_PUT_32BIT_BE (alig_dgst + 6, ctx->H[6]);
+      mhd_PUT_32BIT_BE (alig_dgst + 7, ctx->H[7]);
+      /* Copy result to unaligned destination address */
+      memcpy (digest, alig_dgst, mhd_SHA256_DIGEST_SIZE);
+    }
+    else
+    {
+      /* Use cast to (void*) here to mute compiler alignment warnings.
+       * Compilers are not smart enough to see that alignment has been checked. */
+      mhd_PUT_32BIT_BE ((void *) (digest + 0 * mhd_SHA256_BYTES_IN_WORD), \
+                        ctx->H[0]);
+      mhd_PUT_32BIT_BE ((void *) (digest + 1 * mhd_SHA256_BYTES_IN_WORD), \
+                        ctx->H[1]);
+      mhd_PUT_32BIT_BE ((void *) (digest + 2 * mhd_SHA256_BYTES_IN_WORD), \
+                        ctx->H[2]);
+      mhd_PUT_32BIT_BE ((void *) (digest + 3 * mhd_SHA256_BYTES_IN_WORD), \
+                        ctx->H[3]);
+      mhd_PUT_32BIT_BE ((void *) (digest + 4 * mhd_SHA256_BYTES_IN_WORD), \
+                        ctx->H[4]);
+      mhd_PUT_32BIT_BE ((void *) (digest + 5 * mhd_SHA256_BYTES_IN_WORD), \
+                        ctx->H[5]);
+      mhd_PUT_32BIT_BE ((void *) (digest + 6 * mhd_SHA256_BYTES_IN_WORD), \
+                        ctx->H[6]);
+      mhd_PUT_32BIT_BE ((void *) (digest + 7 * mhd_SHA256_BYTES_IN_WORD), \
+                        ctx->H[7]);
+    }
   }
-#endif /* ! MHD_FAVOR_SMALL_CODE || _MHD_PUT_32BIT_BE_UNALIGNED */
 
   /* Erase potentially sensitive data. */
-  memset (ctx, 0, sizeof(struct Sha256Ctx));
+  memset (ctx, 0, sizeof(struct mhd_Sha256CtxInt));
 }
 
 
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE

+ 121 - 0
src/mhd2/sha256_int.h

@@ -0,0 +1,121 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2019-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/sha256.h
+ * @brief  Calculation of SHA-256 digest
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA256_INT_H
+#define MHD_SHA256_INT_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+
+/**
+ *  Digest is kept internally as 8 32-bit words.
+ */
+#define mhd_SHA256_DIGEST_SIZE_WORDS 8
+
+/**
+ * Number of bits in single SHA-256 word
+ */
+#define mhd_SHA256_WORD_SIZE_BITS 32
+
+/**
+ * Number of bytes in single SHA-256 word
+ * used to process data
+ */
+#define mhd_SHA256_BYTES_IN_WORD (mhd_SHA256_WORD_SIZE_BITS / 8)
+
+/**
+ * Size of SHA-256 digest in bytes
+ */
+#define mhd_SHA256_DIGEST_SIZE \
+        (mhd_SHA256_DIGEST_SIZE_WORDS * mhd_SHA256_BYTES_IN_WORD)
+
+/**
+ * Size of single processing block in bits
+ */
+#define mhd_SHA256_BLOCK_SIZE_BITS 512
+
+/**
+ * Size of single processing block in bytes
+ */
+#define mhd_SHA256_BLOCK_SIZE (mhd_SHA256_BLOCK_SIZE_BITS / 8)
+
+/**
+ * Size of single processing block in bytes
+ */
+#define mhd_SHA256_BLOCK_SIZE_WORDS \
+        (mhd_SHA256_BLOCK_SIZE_BITS / mhd_SHA256_WORD_SIZE_BITS)
+
+
+struct mhd_Sha256CtxInt
+{
+  uint32_t H[mhd_SHA256_DIGEST_SIZE_WORDS];     /**< Intermediate hash value / digest at end of calculation */
+  uint32_t buffer[mhd_SHA256_BLOCK_SIZE_WORDS]; /**< SHA256 input data buffer */
+  uint64_t count;                               /**< number of bytes, mod 2^64 */
+};
+
+/**
+ * Initialise structure for SHA-256 calculation.
+ *
+ * @param ctx must be a `struct mhd_Sha256CtxInt *`
+ */
+MHD_INTERNAL void
+mhd_SHA256_init (struct mhd_Sha256CtxInt *ctx)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param ctx must be a `struct mhd_Sha256CtxInt *`
+ * @param size number of bytes in @a data
+ * @param data bytes to add to hash
+ */
+MHD_INTERNAL void
+mhd_SHA256_update (struct mhd_Sha256CtxInt *restrict ctx,
+                   size_t size,
+                   const uint8_t *restrict data)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3, 2);
+
+
+/**
+ * Finalise SHA256 calculation, return digest.
+ *
+ * @param ctx must be a `struct mhd_Sha256CtxInt *`
+ * @param[out] digest set to the hash, must be #mhd_SHA256_DIGEST_SIZE bytes
+ */
+MHD_INTERNAL void
+mhd_SHA256_finish (struct mhd_Sha256CtxInt *restrict ctx,
+                   uint8_t digest[mhd_SHA256_DIGEST_SIZE])
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+/**
+ * Indicates that function mhd_SHA256_finish() (without context reset) is available
+ */
+#define mhd_SHA256_HAS_FINISH 1
+
+#endif /* MHD_SHA256_INT_H */

+ 129 - 139
src/microhttpd/sha512_256.c → src/mhd2/sha512_256_int.c

@@ -1,44 +1,41 @@
 /*
-     This file is part of GNU libmicrohttpd
-     Copyright (C) 2022-2023 Evgeny Grin (Karlson2k)
-
-     GNU libmicrohttpd is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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, see <http://www.gnu.org/licenses/>.
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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/sha512_256.c
+ * @file src/mhd2/sha512_256.c
  * @brief  Calculation of SHA-512/256 digest as defined in FIPS PUB 180-4 (2015)
  * @author Karlson2k (Evgeny Grin)
  */
 
-#include "sha512_256.h"
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
 
 #include <string.h>
-#ifdef HAVE_MEMORY_H
-#include <memory.h>
-#endif /* HAVE_MEMORY_H */
 #include "mhd_bithelpers.h"
 #include "mhd_assert.h"
 
-/**
- * Initialise structure for SHA-512/256 calculation.
- *
- * @param ctx the calculation context
- */
-void
-MHD_SHA512_256_init (struct Sha512_256Ctx *ctx)
+#include "sha512_256_int.h"
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_SHA512_256_init (struct mhd_Sha512_256CtxInt *ctx)
 {
   /* Initial hash values, see FIPS PUB 180-4 clause 5.3.6.2 */
   /* Values generated by "IV Generation Function" as described in
@@ -58,17 +55,17 @@ MHD_SHA512_256_init (struct Sha512_256Ctx *ctx)
 }
 
 
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE
 
 /**
  * Base of SHA-512/256 transformation.
  * Gets full 128 bytes block of data and updates hash values;
  * @param H     hash values
- * @param data  the data buffer with #SHA512_256_BLOCK_SIZE bytes block
+ * @param data  the data buffer with #mhd_SHA512_256_BLOCK_SIZE bytes block
  */
-static void
-sha512_256_transform (uint64_t H[SHA512_256_HASH_SIZE_WORDS],
-                      const void *data)
+static MHD_FN_PAR_NONNULL_ALL_ void
+sha512_256_transform (uint64_t H[mhd_SHA512_256_HASH_SIZE_WORDS],
+                      const void *restrict data)
 {
   /* Working variables,
      see FIPS PUB 180-4 clause 6.7, 6.4. */
@@ -85,8 +82,8 @@ sha512_256_transform (uint64_t H[SHA512_256_HASH_SIZE_WORDS],
      See FIPS PUB 180-4 clause 5.2.2, 6.7, 6.4. */
   uint64_t W[16];
 
-#ifndef _MHD_GET_64BIT_BE_ALLOW_UNALIGNED
-  if (0 != (((uintptr_t) data) % _MHD_UINT64_ALIGN))
+#ifndef mhd_GET_64BIT_BE_ALLOW_UNALIGNED
+  if (0 != (((uintptr_t) data) % mhd_UINT64_ALIGN))
   { /* The input data is unaligned */
     /* Copy the unaligned input data to the aligned buffer */
     memcpy (W, data, sizeof(W));
@@ -95,7 +92,7 @@ sha512_256_transform (uint64_t H[SHA512_256_HASH_SIZE_WORDS],
      * the next steps */
     data = (const void *) W;
   }
-#endif /* _MHD_GET_64BIT_BE_ALLOW_UNALIGNED */
+#endif /* mhd_GET_64BIT_BE_ALLOW_UNALIGNED */
 
   /* 'Ch' and 'Maj' macro functions are defined with
      widely-used optimisation.
@@ -109,13 +106,13 @@ sha512_256_transform (uint64_t H[SHA512_256_HASH_SIZE_WORDS],
   /* Four 'Sigma' macro functions.
      See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */
 #define SIG0(x)  \
-  ( _MHD_ROTR64 ((x), 28) ^ _MHD_ROTR64 ((x), 34) ^ _MHD_ROTR64 ((x), 39) )
+        (mhd_ROTR64 ((x), 28) ^ mhd_ROTR64 ((x), 34) ^ mhd_ROTR64 ((x), 39) )
 #define SIG1(x)  \
-  ( _MHD_ROTR64 ((x), 14) ^ _MHD_ROTR64 ((x), 18) ^ _MHD_ROTR64 ((x), 41) )
+        (mhd_ROTR64 ((x), 14) ^ mhd_ROTR64 ((x), 18) ^ mhd_ROTR64 ((x), 41) )
 #define sig0(x)  \
-  ( _MHD_ROTR64 ((x), 1) ^ _MHD_ROTR64 ((x), 8) ^ ((x) >> 7) )
+        (mhd_ROTR64 ((x), 1) ^ mhd_ROTR64 ((x), 8) ^ ((x) >> 7) )
 #define sig1(x)  \
-  ( _MHD_ROTR64 ((x), 19) ^ _MHD_ROTR64 ((x), 61) ^ ((x) >> 6) )
+        (mhd_ROTR64 ((x), 19) ^ mhd_ROTR64 ((x), 61) ^ ((x) >> 6) )
 
   /* One step of SHA-512/256 computation,
      see FIPS PUB 180-4 clause 6.4.2 step 3.
@@ -127,15 +124,15 @@ sha512_256_transform (uint64_t H[SHA512_256_HASH_SIZE_WORDS],
    * Note: 'wt' must be used exactly one time in this macro as it change other
            data as well every time when used. */
 #define SHA2STEP64(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do {                  \
-    (vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt));  \
-    (vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0)
+          (vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt));  \
+          (vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0)
 
   /* Get value of W(t) from input data buffer for 0 <= t <= 15,
      See FIPS PUB 180-4 clause 6.2.
      Input data must be read in big-endian bytes order,
      see FIPS PUB 180-4 clause 3.1.2. */
 #define GET_W_FROM_DATA(buf,t) \
-  _MHD_GET_64BIT_BE (((const uint64_t*) (buf)) + (t))
+        mhd_GET_64BIT_BE (((const uint64_t*) (buf)) + (t))
 
   /* 'W' generation and assignment for 16 <= t <= 79.
      See FIPS PUB 180-4 clause 6.4.2.
@@ -156,7 +153,7 @@ sha512_256_transform (uint64_t H[SHA512_256_HASH_SIZE_WORDS],
              SHA2STEP64(h, a, b, c, d, e, f, g, K[1], data[1]);
            so current 'vD' will be used as 'vE' on next step,
            current 'vH' will be used as 'vA' on next step. */
-#if _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
+#if mhd_BYTE_ORDER == mhd_BIG_ENDIAN
   if ((const void *) W == data)
   {
     /* The input data is already in the cyclic data buffer W[] in correct bytes
@@ -179,7 +176,7 @@ sha512_256_transform (uint64_t H[SHA512_256_HASH_SIZE_WORDS],
     SHA2STEP64 (b, c, d, e, f, g, h, a, UINT64_C (0xc19bf174cf692694), W[15]);
   }
   else /* Combined with the next 'if' */
-#endif /* _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN */
+#endif /* mhd_BYTE_ORDER == mhd_BIG_ENDIAN */
   if (1)
   {
     /* During first 16 steps, before making any calculations on each step,
@@ -405,17 +402,20 @@ sha512_256_transform (uint64_t H[SHA512_256_HASH_SIZE_WORDS],
        see FIPS PUB 180-4 clause 6.4.2 step 3.
      * Note: this version of macro reassign all working variable on
              each step. */
-#define SHA2STEP64RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do {              \
-  uint64_t tmp_h_ = (vH);                                             \
-  SHA2STEP64((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt));    \
-  (vH) = (vG);                                                        \
-  (vG) = (vF);                                                        \
-  (vF) = (vE);                                                        \
-  (vE) = (vD);                                                        \
-  (vD) = (vC);                                                        \
-  (vC) = (vB);                                                        \
-  (vB) = (vA);                                                        \
-  (vA) = tmp_h_;  } while (0)
+#define SHA2STEP64RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) \
+        do {             \
+          uint64_t tmp_h_ = (vH);                                           \
+          SHA2STEP64 ((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt)); \
+          (vH) = (vG);   \
+          (vG) = (vF);   \
+          (vF) = (vE);   \
+          (vE) = (vD);   \
+          (vD) = (vC);   \
+          (vC) = (vB);   \
+          (vB) = (vA);   \
+          (vA) = tmp_h_; \
+        } \
+        while (0)
 
     /* During first 16 steps, before making any calculations on each step,
        the W element is read from the input data buffer as big-endian value and
@@ -449,34 +449,28 @@ sha512_256_transform (uint64_t H[SHA512_256_HASH_SIZE_WORDS],
 }
 
 
-/**
- * Process portion of bytes.
- *
- * @param ctx the calculation context
- * @param data bytes to add to hash
- * @param length number of bytes in @a data
- */
-void
-MHD_SHA512_256_update (struct Sha512_256Ctx *ctx,
-                       const uint8_t *data,
-                       size_t length)
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_IN_SIZE_ (3, 2) void
+mhd_SHA512_256_update (struct mhd_Sha512_256CtxInt *restrict ctx,
+                       size_t size,
+                       const uint8_t *restrict data)
 {
   unsigned int bytes_have; /**< Number of bytes in the context buffer */
   uint64_t count_hi; /**< The high part to be moved to another variable */
 
-  mhd_assert ((data != NULL) || (length == 0));
+  mhd_assert ((data != NULL) || (size == 0));
 
 #ifndef MHD_FAVOR_SMALL_CODE
-  if (0 == length)
+  if (0 == size)
     return; /* Shortcut, do nothing */
 #endif /* ! MHD_FAVOR_SMALL_CODE */
 
-  /* Note: (count & (SHA512_256_BLOCK_SIZE-1))
-           equals (count % SHA512_256_BLOCK_SIZE) for this block size. */
-  bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1));
-  ctx->count += length;
+  /* Note: (count & (mhd_SHA512_256_BLOCK_SIZE-1))
+           equals (count % mhd_SHA512_256_BLOCK_SIZE) for this block size. */
+  bytes_have = (unsigned int) (ctx->count & (mhd_SHA512_256_BLOCK_SIZE - 1));
+  ctx->count += size;
 #if SIZEOF_SIZE_T > 7
-  if (length > ctx->count)
+  if (size > ctx->count)
     ctx->count_bits_hi += 1U << 3; /* Value wrap */
 #endif /* SIZEOF_SIZE_T > 7 */
   count_hi = ctx->count >> 61;
@@ -488,32 +482,32 @@ MHD_SHA512_256_update (struct Sha512_256Ctx *ctx,
 
   if (0 != bytes_have)
   {
-    unsigned int bytes_left = SHA512_256_BLOCK_SIZE - bytes_have;
-    if (length >= bytes_left)
+    unsigned int bytes_left = mhd_SHA512_256_BLOCK_SIZE - bytes_have;
+    if (size >= bytes_left)
     {     /* Combine new data with data in the buffer and
              process the full block. */
       memcpy (((uint8_t *) ctx->buffer) + bytes_have,
               data,
               bytes_left);
       data += bytes_left;
-      length -= bytes_left;
+      size -= bytes_left;
       sha512_256_transform (ctx->H, ctx->buffer);
       bytes_have = 0;
     }
   }
 
-  while (SHA512_256_BLOCK_SIZE <= length)
+  while (mhd_SHA512_256_BLOCK_SIZE <= size)
   {   /* Process any full blocks of new data directly,
          without copying to the buffer. */
     sha512_256_transform (ctx->H, data);
-    data += SHA512_256_BLOCK_SIZE;
-    length -= SHA512_256_BLOCK_SIZE;
+    data += mhd_SHA512_256_BLOCK_SIZE;
+    size -= mhd_SHA512_256_BLOCK_SIZE;
   }
 
-  if (0 != length)
+  if (0 != size)
   {   /* Copy incomplete block of new data (if any)
          to the buffer. */
-    memcpy (((uint8_t *) ctx->buffer) + bytes_have, data, length);
+    memcpy (((uint8_t *) ctx->buffer) + bytes_have, data, size);
   }
 }
 
@@ -529,15 +523,9 @@ MHD_SHA512_256_update (struct Sha512_256Ctx *ctx,
  */
 #define SHA512_256_SIZE_OF_LEN_ADD (SHA512_256_SIZE_OF_LEN_ADD_BITS / 8)
 
-/**
- * Finalise SHA-512/256 calculation, return digest.
- *
- * @param ctx the calculation context
- * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
- */
-void
-MHD_SHA512_256_finish (struct Sha512_256Ctx *ctx,
-                       uint8_t digest[SHA512_256_DIGEST_SIZE])
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_SHA512_256_finish (struct mhd_Sha512_256CtxInt *restrict ctx,
+                       uint8_t digest[mhd_SHA512_256_DIGEST_SIZE])
 {
   uint64_t num_bits;   /**< Number of processed bits */
   unsigned int bytes_have; /**< Number of bytes in the context buffer */
@@ -547,9 +535,9 @@ MHD_SHA512_256_finish (struct Sha512_256Ctx *ctx,
      not change the amount of hashed data. */
   num_bits = ctx->count << 3;
 
-  /* Note: (count & (SHA512_256_BLOCK_SIZE-1))
-           equals (count % SHA512_256_BLOCK_SIZE) for this block size. */
-  bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1));
+  /* Note: (count & (mhd_SHA512_256_BLOCK_SIZE-1))
+           equals (count % mhd_SHA512_256_BLOCK_SIZE) for this block size. */
+  bytes_have = (unsigned int) (ctx->count & (mhd_SHA512_256_BLOCK_SIZE - 1));
 
   /* Input data must be padded with a single bit "1", then with zeros and
      the finally the length of data in bits must be added as the final bytes
@@ -563,12 +551,12 @@ MHD_SHA512_256_finish (struct Sha512_256Ctx *ctx,
      processed immediately). */
   ((uint8_t *) ctx->buffer)[bytes_have++] = 0x80;
 
-  if (SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD)
+  if (mhd_SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD)
   {   /* No space in the current block to put the total length of message.
          Pad the current block with zeros and process it. */
-    if (bytes_have < SHA512_256_BLOCK_SIZE)
+    if (bytes_have < mhd_SHA512_256_BLOCK_SIZE)
       memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
-              SHA512_256_BLOCK_SIZE - bytes_have);
+              mhd_SHA512_256_BLOCK_SIZE - bytes_have);
     /* Process the full block. */
     sha512_256_transform (ctx->H, ctx->buffer);
     /* Start the new block. */
@@ -577,62 +565,64 @@ MHD_SHA512_256_finish (struct Sha512_256Ctx *ctx,
 
   /* Pad the rest of the buffer with zeros. */
   memset (((uint8_t *) ctx->buffer) + bytes_have, 0,
-          SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have);
+          mhd_SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have);
   /* Put high part of number of bits in processed message and then lower
      part of number of bits as big-endian values.
      See FIPS PUB 180-4 clause 5.1.2. */
   /* Note: the target location is predefined and buffer is always aligned */
-  _MHD_PUT_64BIT_BE (ctx->buffer + SHA512_256_BLOCK_SIZE_WORDS - 2,
-                     ctx->count_bits_hi);
-  _MHD_PUT_64BIT_BE (ctx->buffer + SHA512_256_BLOCK_SIZE_WORDS - 1,
-                     num_bits);
+  mhd_PUT_64BIT_BE (ctx->buffer + mhd_SHA512_256_BLOCK_SIZE_WORDS - 2,
+                    ctx->count_bits_hi);
+  mhd_PUT_64BIT_BE (ctx->buffer + mhd_SHA512_256_BLOCK_SIZE_WORDS - 1,
+                    num_bits);
   /* Process the full final block. */
   sha512_256_transform (ctx->H, ctx->buffer);
 
   /* Put in BE mode the leftmost part of the hash as the final digest.
      See FIPS PUB 180-4 clause 6.7. */
-#ifndef _MHD_PUT_64BIT_BE_UNALIGNED
-  if (1
-#ifndef MHD_FAVOR_SMALL_CODE
-      && (0 != ((uintptr_t) digest) % _MHD_UINT64_ALIGN)
-#endif /* MHD_FAVOR_SMALL_CODE */
-      )
-  {
-    /* If storing of the final result requires aligned address and
-       the destination address is not aligned or compact code is used,
-       store the final digest in aligned temporary buffer first, then
-       copy it to the destination. */
-    uint64_t alig_dgst[SHA512_256_DIGEST_SIZE_WORDS];
-    _MHD_PUT_64BIT_BE (alig_dgst + 0, ctx->H[0]);
-    _MHD_PUT_64BIT_BE (alig_dgst + 1, ctx->H[1]);
-    _MHD_PUT_64BIT_BE (alig_dgst + 2, ctx->H[2]);
-    _MHD_PUT_64BIT_BE (alig_dgst + 3, ctx->H[3]);
-    /* Copy result to the unaligned destination address */
-    memcpy (digest, alig_dgst, SHA512_256_DIGEST_SIZE);
-  }
-#ifndef MHD_FAVOR_SMALL_CODE
-  else /* Combined with the next 'if' */
-#endif /* MHD_FAVOR_SMALL_CODE */
-#endif /* ! _MHD_PUT_64BIT_BE_UNALIGNED */
-#if ! defined(MHD_FAVOR_SMALL_CODE) || defined(_MHD_PUT_64BIT_BE_UNALIGNED)
   if (1)
   {
-    /* Use cast to (void*) here to mute compiler alignment warnings.
-     * Compilers are not smart enough to see that alignment has been checked. */
-    _MHD_PUT_64BIT_BE ((void *) (digest + 0 * SHA512_256_BYTES_IN_WORD), \
-                       ctx->H[0]);
-    _MHD_PUT_64BIT_BE ((void *) (digest + 1 * SHA512_256_BYTES_IN_WORD), \
-                       ctx->H[1]);
-    _MHD_PUT_64BIT_BE ((void *) (digest + 2 * SHA512_256_BYTES_IN_WORD), \
-                       ctx->H[2]);
-    _MHD_PUT_64BIT_BE ((void *) (digest + 3 * SHA512_256_BYTES_IN_WORD), \
-                       ctx->H[3]);
+    bool use_tmp_buf_to_align_result;
+
+#if defined(mhd_PUT_64BIT_BE_UNALIGNED)
+    use_tmp_buf_to_align_result = false;
+#elif defined (MHD_FAVOR_SMALL_CODE)
+    use_tmp_buf_to_align_result = true; /* smaller code: eliminated branch below */
+#else
+    use_tmp_buf_to_align_result =
+      (0 != ((uintptr_t) digest) % mhd_UINT64_ALIGN);
+#endif
+    if (use_tmp_buf_to_align_result)
+    {
+      /* If storing of the final result requires aligned address and
+         the destination address is not aligned or compact code is used,
+         store the final digest in aligned temporary buffer first, then
+         copy it to the destination. */
+      uint64_t alig_dgst[mhd_SHA512_256_DIGEST_SIZE_WORDS];
+      mhd_PUT_64BIT_BE (alig_dgst + 0, ctx->H[0]);
+      mhd_PUT_64BIT_BE (alig_dgst + 1, ctx->H[1]);
+      mhd_PUT_64BIT_BE (alig_dgst + 2, ctx->H[2]);
+      mhd_PUT_64BIT_BE (alig_dgst + 3, ctx->H[3]);
+      /* Copy result to the unaligned destination address */
+      memcpy (digest, alig_dgst, mhd_SHA512_256_DIGEST_SIZE);
+    }
+    else
+    {
+      /* Use cast to (void*) here to mute compiler alignment warnings.
+       * Compilers are not smart enough to see that alignment has been checked. */
+      mhd_PUT_64BIT_BE ((void *) (digest + 0 * mhd_SHA512_256_BYTES_IN_WORD), \
+                        ctx->H[0]);
+      mhd_PUT_64BIT_BE ((void *) (digest + 1 * mhd_SHA512_256_BYTES_IN_WORD), \
+                        ctx->H[1]);
+      mhd_PUT_64BIT_BE ((void *) (digest + 2 * mhd_SHA512_256_BYTES_IN_WORD), \
+                        ctx->H[2]);
+      mhd_PUT_64BIT_BE ((void *) (digest + 3 * mhd_SHA512_256_BYTES_IN_WORD), \
+                        ctx->H[3]);
+    }
   }
-#endif /* ! MHD_FAVOR_SMALL_CODE || _MHD_PUT_64BIT_BE_UNALIGNED */
 
   /* Erase potentially sensitive data. */
-  memset (ctx, 0, sizeof(struct Sha512_256Ctx));
+  memset (ctx, 0, sizeof(struct mhd_Sha512_256CtxInt));
 }
 
 
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
+mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE

+ 140 - 0
src/mhd2/sha512_256_int.h

@@ -0,0 +1,140 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU libmicrohttpd 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 src/mhd2/sha512_256.h
+ * @brief  Calculation of SHA-512/256 digest
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA512_256_INT_H
+#define MHD_SHA512_256_INT_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+
+
+/**
+ * Number of bits in single SHA-512/256 word.
+ */
+#define mhd_SHA512_256_WORD_SIZE_BITS 64
+
+/**
+ * Number of bytes in single SHA-512/256 word.
+ */
+#define mhd_SHA512_256_BYTES_IN_WORD (mhd_SHA512_256_WORD_SIZE_BITS / 8)
+
+/**
+ * Hash is kept internally as 8 64-bit words.
+ * This is intermediate hash size, used during computing the final digest.
+ */
+#define mhd_SHA512_256_HASH_SIZE_WORDS 8
+
+/**
+ * Size of SHA-512/256 resulting digest in words.
+ * This is the final digest size, not intermediate hash.
+ */
+#define mhd_SHA512_256_DIGEST_SIZE_WORDS (mhd_SHA512_256_HASH_SIZE_WORDS  / 2)
+
+/**
+ * Size of SHA-512/256 resulting digest in bytes.
+ * This is the final digest size, not intermediate hash.
+ */
+#define mhd_SHA512_256_DIGEST_SIZE \
+        (mhd_SHA512_256_DIGEST_SIZE_WORDS * mhd_SHA512_256_BYTES_IN_WORD)
+
+/**
+ * Size of SHA-512/256 single processing block in bits.
+ */
+#define mhd_SHA512_256_BLOCK_SIZE_BITS 1024
+
+/**
+ * Size of SHA-512/256 single processing block in bytes.
+ */
+#define mhd_SHA512_256_BLOCK_SIZE (mhd_SHA512_256_BLOCK_SIZE_BITS / 8)
+
+/**
+ * Size of SHA-512/256 single processing block in words.
+ */
+#define mhd_SHA512_256_BLOCK_SIZE_WORDS \
+        (mhd_SHA512_256_BLOCK_SIZE_BITS / mhd_SHA512_256_WORD_SIZE_BITS)
+
+
+/**
+ * SHA-512/256 calculation context
+ */
+struct mhd_Sha512_256CtxInt
+{
+  uint64_t H[mhd_SHA512_256_HASH_SIZE_WORDS];       /**< Intermediate hash value  */
+  uint64_t buffer[mhd_SHA512_256_BLOCK_SIZE_WORDS]; /**< SHA512_256 input data buffer */
+  /**
+   * The number of bytes, lower part
+   */
+  uint64_t count;
+  /**
+   * The number of bits, high part.
+   * Unlike lower part, this counts the number of bits, not bytes.
+   */
+  uint64_t count_bits_hi;
+};
+
+/**
+ * Initialise structure for SHA-512/256 calculation.
+ *
+ * @param ctx the calculation context
+ */
+MHD_INTERNAL void
+mhd_SHA512_256_init (struct mhd_Sha512_256CtxInt *ctx)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param ctx the calculation context
+ * @param size number of bytes in @a data
+ * @param data bytes to add to hash
+ */
+MHD_INTERNAL void
+mhd_SHA512_256_update (struct mhd_Sha512_256CtxInt *restrict ctx,
+                       size_t size,
+                       const uint8_t *restrict data)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3, 2);
+
+
+/**
+ * Finalise SHA-512/256 calculation, return digest.
+ *
+ * @param ctx the calculation context
+ * @param[out] digest set to the hash, must be #mhd_SHA512_256_DIGEST_SIZE bytes
+ */
+MHD_INTERNAL void
+mhd_SHA512_256_finish (struct mhd_Sha512_256CtxInt *restrict ctx,
+                       uint8_t digest[mhd_SHA512_256_DIGEST_SIZE])
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2);
+
+/**
+ * Indicates that function mhd_SHA512_256_finish() (without context reset) is
+ * available
+ */
+#define mhd_SHA512_256_HAS_FINISH 1
+
+#endif /* MHD_SHA512_256_H */

+ 8 - 0
src/mhd2/stream_funcs.c

@@ -738,6 +738,14 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c,
     end_code = MHD_REQUEST_ENDED_BY_APP_ERROR;
     sc = MHD_SC_REPLY_FILE_TOO_SHORT;
     break;
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+  case mhd_CONN_CLOSE_NONCE_ERROR:
+    close_hard = true;
+    end_code = MHD_REQUEST_ENDED_NONCE_ERROR;
+    sc = MHD_SC_REPLY_NONCE_ERROR;
+    break;
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
   case mhd_CONN_CLOSE_INT_ERROR:
     close_hard = true;
     end_code = MHD_REQUEST_ENDED_NO_RESOURCES;

+ 8 - 1
src/mhd2/stream_funcs.h

@@ -194,7 +194,7 @@ enum mhd_ConnCloseReason
   mhd_CONN_CLOSE_APP_ERROR
   ,
   /**
-   * Application requested about of the stream
+   * Application requested abort of the stream
    */
   mhd_CONN_CLOSE_APP_ABORTED
   ,
@@ -213,6 +213,13 @@ enum mhd_ConnCloseReason
    */
   mhd_CONN_CLOSE_FILE_TOO_SHORT
   ,
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+  /**
+   * Error generating nonce for Digest Auth
+   */
+  mhd_CONN_CLOSE_NONCE_ERROR
+  ,
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
 
   /* Hard problem while receiving or sending */
   /**

+ 55 - 0
src/mhd2/stream_process_reply.c

@@ -54,6 +54,10 @@
 #include "stream_process_reply.h"
 #include "stream_funcs.h"
 #include "request_get_value.h"
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+#  include "mhd_digest_auth_data.h"
+#  include "auth_digest.h"
+#endif
 
 #include "mhd_read_file.h"
 
@@ -631,6 +635,21 @@ add_user_headers (char *restrict buf,
 #define buffer_append_s(buf,ppos,buf_size,str) \
         buffer_append (buf,ppos,buf_size,str, mhd_SSTR_LEN (str))
 
+
+/**
+ * Append MHD_String to the buffer if enough space is available,
+ * update position.
+ * @param[out] buf the buffer to append data to
+ * @param[in,out] ppos the pointer to position in the @a buffer
+ * @param buf_size the size of the @a buffer
+ * @param pmhdstr the pointer to string to append
+ * @return true if data has been added and position has been updated,
+ *         false if not enough space is available
+ */
+#define buffer_append_mstr(buf,ppos,buf_size,pmhdstr) \
+        buffer_append ((buf),(ppos),(buf_size), \
+                       (pmhdstr)->cstr, (pmhdstr)->len)
+
 /**
  * Allocate the connection's write buffer and fill it with all of the
  * headers from the response.
@@ -816,6 +835,36 @@ build_header_response_inn (struct MHD_Connection *restrict c)
     use_conn_k_alive = false;
   }
 
+  /* Special headers */
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+  if (mhd_RESP_HAD_AUTH_DIGEST (r))
+  {
+    char noncestr[mhd_AUTH_DIGEST_NONCE_LEN];
+    const struct mhd_RespAuthDigestHeader *dg_hdr;
+    if (! mhd_auth_digest_get_new_nonce (c,
+                                         noncestr))
+    {
+      mhd_STREAM_ABORT (c,
+                        mhd_CONN_CLOSE_NONCE_ERROR,
+                        "Failed to generate a new nonce for Digest Auth.");
+      return false;
+    }
+    for (dg_hdr = mhd_DLINKEDL_GET_FIRST (r, auth_d_hdrs);
+         NULL != dg_hdr;
+         dg_hdr = mhd_DLINKEDL_GET_NEXT (dg_hdr, auth_d_hdrs))
+    {
+      size_t nonce_pos;
+      nonce_pos = pos + dg_hdr->nonce_pos;
+      if (! buffer_append_mstr (buf, &pos, buf_size, \
+                                &(dg_hdr->hdr)))
+        return false;
+      memcpy (buf + nonce_pos,
+              noncestr,
+              sizeof(noncestr));
+    }
+  }
+#endif /* MHD_SUPPORT_AUTH_DIGEST */
+
   /* User-defined headers */
 
   if (! add_user_headers (buf, &pos, buf_size, r,
@@ -888,6 +937,12 @@ mhd_stream_build_header_response (struct MHD_Connection *restrict c)
 {
   if (! build_header_response_inn (c))
   {
+#ifdef MHD_SUPPORT_AUTH_DIGEST
+    if (mhd_HTTP_STAGE_PRE_CLOSING <= c->stage)
+      return false; /* Already started closing */
+#else  /* ! MHD_SUPPORT_AUTH_DIGEST */
+    mhd_assert (mhd_HTTP_STAGE_START_REPLY == c->stage);
+#endif /* ! MHD_SUPPORT_AUTH_DIGEST */
     mhd_STREAM_ABORT (c,
                       mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
                       "No memory in the pool for the reply headers.");

+ 1 - 1
src/mhd2/stream_process_request.c

@@ -1141,7 +1141,7 @@ request_add_get_arg (void *restrict cls,
 
 MHD_INTERNAL
 MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
-MHD_FN_PAR_INOUT_ (2) bool
+MHD_FN_PAR_INOUT_SIZE_ (2, 1) bool
 // TODO: detect and report errors
 mhd_parse_get_args (size_t args_len,
                     char *restrict args,

+ 1 - 1
src/mhd2/stream_process_request.h

@@ -74,7 +74,7 @@ mhd_parse_get_args (size_t args_len,
                     char *restrict args,
                     mhd_GetArgumentInter cb,
                     void *restrict cls)
-MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_INOUT_ (2);
+MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_INOUT_SIZE_ (2, 1);
 
 
 /**

+ 0 - 4082
src/microhttpd/digestauth.c

@@ -1,4082 +0,0 @@
-/*
-     This file is part of libmicrohttpd
-     Copyright (C) 2010, 2011, 2012, 2015, 2018 Daniel Pittman and Christian Grothoff
-     Copyright (C) 2014-2024 Evgeny Grin (Karlson2k)
-
-     This library is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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 digestauth.c
- * @brief Implements HTTP digest authentication
- * @author Amr Ali
- * @author Matthieu Speder
- * @author Christian Grothoff (RFC 7616 support)
- * @author Karlson2k (Evgeny Grin) (fixes, new API, improvements, large rewrite,
- *                                  many RFC 7616 features implementation,
- *                                  old RFC 2069 support)
- */
-#include "digestauth.h"
-#include "gen_auth.h"
-#include "platform.h"
-#include "mhd_limits.h"
-#include "internal.h"
-#include "response.h"
-#ifdef MHD_MD5_SUPPORT
-#  include "mhd_md5_wrap.h"
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-#  include "mhd_sha256_wrap.h"
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-#  include "sha512_256.h"
-#endif /* MHD_SHA512_256_SUPPORT */
-#include "mhd_locks.h"
-#include "mhd_mono_clock.h"
-#include "mhd_str.h"
-#include "mhd_compat.h"
-#include "mhd_bithelpers.h"
-#include "mhd_assert.h"
-
-
-/**
- * Allow re-use of the nonce-nc map array slot after #REUSE_TIMEOUT seconds,
- * if this slot is needed for the new nonce, while the old nonce was not used
- * even one time by the client.
- * Typically clients immediately use generated nonce for new request.
- */
-#define REUSE_TIMEOUT 30
-
-/**
- * The maximum value of artificial timestamp difference to avoid clashes.
- * The value must be suitable for bitwise AND operation.
- */
-#define DAUTH_JUMPBACK_MAX (0x7F)
-
-
-/**
- * 48 bit value in bytes
- */
-#define TIMESTAMP_BIN_SIZE (48 / 8)
-
-
-/**
- * Trim value to the TIMESTAMP_BIN_SIZE size
- */
-#define TRIM_TO_TIMESTAMP(value) \
-  ((value) & ((UINT64_C (1) << (TIMESTAMP_BIN_SIZE * 8)) - 1))
-
-
-/**
- * The printed timestamp size in chars
- */
-#define TIMESTAMP_CHARS_LEN (TIMESTAMP_BIN_SIZE * 2)
-
-
-/**
- * Standard server nonce length, not including terminating null,
- *
- * @param digest_size digest size
- */
-#define NONCE_STD_LEN(digest_size) \
-  ((digest_size) * 2 + TIMESTAMP_CHARS_LEN)
-
-
-#ifdef MHD_SHA512_256_SUPPORT
-/**
- * Maximum size of any digest hash supported by MHD.
- * (SHA-512/256 > MD5).
- */
-#define MAX_DIGEST SHA512_256_DIGEST_SIZE
-
-/**
- * The common size of SHA-256 digest and SHA-512/256 digest
- */
-#define SHA256_SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_SIZE
-#elif defined(MHD_SHA256_SUPPORT)
-/**
- * Maximum size of any digest hash supported by MHD.
- * (SHA-256 > MD5).
- */
-#define MAX_DIGEST SHA256_DIGEST_SIZE
-
-/**
- * The common size of SHA-256 digest and SHA-512/256 digest
- */
-#define SHA256_SHA512_256_DIGEST_SIZE SHA256_DIGEST_SIZE
-#elif defined(MHD_MD5_SUPPORT)
-/**
- * Maximum size of any digest hash supported by MHD.
- */
-#define MAX_DIGEST MD5_DIGEST_SIZE
-#else  /* ! MHD_MD5_SUPPORT */
-#error At least one hashing algorithm must be enabled
-#endif /* ! MHD_MD5_SUPPORT */
-
-
-/**
- * Macro to avoid using VLAs if the compiler does not support them.
- */
-#ifndef HAVE_C_VARARRAYS
-/**
- * Return #MAX_DIGEST.
- *
- * @param n length of the digest to be used for a VLA
- */
-#define VLA_ARRAY_LEN_DIGEST(n) (MAX_DIGEST)
-
-#else
-/**
- * Return @a n.
- *
- * @param n length of the digest to be used for a VLA
- */
-#define VLA_ARRAY_LEN_DIGEST(n) (n)
-#endif
-
-/**
- * Check that @a n is below #MAX_DIGEST
- */
-#define VLA_CHECK_LEN_DIGEST(n) \
-  do { if ((n) > MAX_DIGEST) MHD_PANIC (_ ("VLA too big.\n")); } while (0)
-
-/**
- * Maximum length of a username for digest authentication.
- */
-#define MAX_USERNAME_LENGTH 128
-
-/**
- * Maximum length of a realm for digest authentication.
- */
-#define MAX_REALM_LENGTH 256
-
-/**
- * Maximum length of the response in digest authentication.
- */
-#define MAX_AUTH_RESPONSE_LENGTH (MAX_DIGEST * 2)
-
-/**
- * The required prefix of parameter with the extended notation
- */
-#define MHD_DAUTH_EXT_PARAM_PREFIX "UTF-8'"
-
-/**
- * The minimal size of the prefix for parameter with the extended notation
- */
-#define MHD_DAUTH_EXT_PARAM_MIN_LEN \
-  MHD_STATICSTR_LEN_ (MHD_DAUTH_EXT_PARAM_PREFIX "'")
-
-/**
- * The result of nonce-nc map array check.
- */
-enum MHD_CheckNonceNC_
-{
-  /**
-   * The nonce and NC are OK (valid and NC was not used before).
-   */
-  MHD_CHECK_NONCENC_OK = MHD_DAUTH_OK,
-
-  /**
-   * The 'nonce' was overwritten with newer 'nonce' in the same slot or
-   * NC was already used.
-   * The validity of the 'nonce' was not be checked.
-   */
-  MHD_CHECK_NONCENC_STALE = MHD_DAUTH_NONCE_STALE,
-
-  /**
-   * The 'nonce' is wrong, it was not generated before.
-   */
-  MHD_CHECK_NONCENC_WRONG = MHD_DAUTH_NONCE_WRONG
-};
-
-
-/**
- * Get base hash calculation algorithm from #MHD_DigestAuthAlgo3 value.
- * @param algo3 the MHD_DigestAuthAlgo3 value
- * @return the base hash calculation algorithm
- */
-_MHD_static_inline enum MHD_DigestBaseAlgo
-get_base_digest_algo (enum MHD_DigestAuthAlgo3 algo3)
-{
-  unsigned int base_algo;
-
-  base_algo =
-    ((unsigned int) algo3)
-    & ~((unsigned int)
-        (MHD_DIGEST_AUTH_ALGO3_NON_SESSION
-         | MHD_DIGEST_AUTH_ALGO3_SESSION));
-  return (enum MHD_DigestBaseAlgo) base_algo;
-}
-
-
-/**
- * Get digest size for specified algorithm.
- *
- * Internal inline version.
- * @param algo3 the algorithm to check
- * @return the size of the digest or zero if the input value is not
- *         supported/valid
- */
-_MHD_static_inline size_t
-digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3)
-{
-#ifdef MHD_MD5_SUPPORT
-  mhd_assert (MHD_MD5_DIGEST_SIZE == MD5_DIGEST_SIZE);
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-  mhd_assert (MHD_SHA256_DIGEST_SIZE == SHA256_DIGEST_SIZE);
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-  mhd_assert (MHD_SHA512_256_DIGEST_SIZE == SHA512_256_DIGEST_SIZE);
-#ifdef MHD_SHA256_SUPPORT
-  mhd_assert (SHA256_DIGEST_SIZE == SHA512_256_DIGEST_SIZE);
-#endif /* MHD_SHA256_SUPPORT */
-#endif /* MHD_SHA512_256_SUPPORT */
-  /* Only one algorithm must be specified */
-  mhd_assert (1 == \
-              (((0 != (algo3 & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0)   \
-               + ((0 != (algo3 & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0)   \
-               + ((0 != (algo3 & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0)));
-#ifdef MHD_MD5_SUPPORT
-  if (0 != (((unsigned int) algo3)
-            & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5)))
-    return MHD_MD5_DIGEST_SIZE;
-  else
-#endif /* MHD_MD5_SUPPORT */
-#if defined(MHD_SHA256_SUPPORT) && defined(MHD_SHA512_256_SUPPORT)
-  if (0 != (((unsigned int) algo3)
-            & ( ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256)
-                | ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA512_256))))
-    return MHD_SHA256_DIGEST_SIZE; /* The same as SHA512_256_DIGEST_SIZE */
-  else
-#elif defined(MHD_SHA256_SUPPORT)
-  if (0 != (((unsigned int) algo3)
-            & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256)))
-    return MHD_SHA256_DIGEST_SIZE;
-  else
-#elif defined(MHD_SHA512_256_SUPPORT)
-  if (0 != (((unsigned int) algo3)
-            & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA512_256)))
-    return MHD_SHA512_256_DIGEST_SIZE;
-  else
-#endif /* MHD_SHA512_256_SUPPORT */
-    (void) 0; /* Unsupported algorithm */
-
-  return 0; /* Wrong input or unsupported algorithm */
-}
-
-
-/**
- * Get digest size for specified algorithm.
- *
- * The size of the digest specifies the size of the userhash, userdigest
- * and other parameters which size depends on used hash algorithm.
- * @param algo3 the algorithm to check
- * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
- *         #MHD_SHA256_DIGEST_SIZE/MHD_SHA512_256_DIGEST_SIZE)
- *         or zero if the input value is not supported or not valid
- * @sa #MHD_digest_auth_calc_userdigest()
- * @sa #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_calc_userhash_hex()
- * @note Available since #MHD_VERSION 0x00097701
- * @ingroup authentication
- */
-_MHD_EXTERN size_t
-MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3)
-{
-  return digest_get_hash_size (algo3);
-}
-
-
-/**
- * Digest context data
- */
-union DigestCtx
-{
-#ifdef MHD_MD5_SUPPORT
-  struct Md5CtxWr md5_ctx;
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-  struct Sha256CtxWr sha256_ctx;
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-  struct Sha512_256Ctx sha512_256_ctx;
-#endif /* MHD_SHA512_256_SUPPORT */
-};
-
-/**
- * The digest calculation structure.
- */
-struct DigestAlgorithm
-{
-  /**
-   * A context for the digest algorithm, already initialized to be
-   * useful for @e init, @e update and @e digest.
-   */
-  union DigestCtx ctx;
-
-  /**
-   * The hash calculation algorithm.
-   */
-  enum MHD_DigestBaseAlgo algo;
-
-  /**
-   * Buffer for hex-print of the final digest.
-   */
-#ifdef _DEBUG
-  bool uninitialised; /**< The structure has been not set-up */
-  bool algo_selected; /**< The algorithm has been selected */
-  bool ready_for_hashing; /**< The structure is ready to hash data */
-  bool hashing; /**< Some data has been hashed, but the digest has not finalised yet */
-#endif /* _DEBUG */
-};
-
-
-/**
- * Return the size of the digest.
- * @param da the digest calculation structure to identify
- * @return the size of the digest.
- */
-_MHD_static_inline unsigned int
-digest_get_size (struct DigestAlgorithm *da)
-{
-  mhd_assert (! da->uninitialised);
-  mhd_assert (da->algo_selected);
-#ifdef MHD_MD5_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
-    return MD5_DIGEST_SIZE;
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
-    return SHA256_DIGEST_SIZE;
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
-    return SHA512_256_DIGEST_SIZE;
-#endif /* MHD_SHA512_256_SUPPORT */
-  mhd_assert (0); /* May not happen */
-  return 0;
-}
-
-
-#if defined(MHD_MD5_HAS_DEINIT) || defined(MHD_SHA256_HAS_DEINIT)
-/**
- * Indicates presence of digest_deinit() function
- */
-#define MHD_DIGEST_HAS_DEINIT 1
-#endif /* MHD_MD5_HAS_DEINIT || MHD_SHA256_HAS_DEINIT */
-
-#ifdef MHD_DIGEST_HAS_DEINIT
-/**
- * Zero-initialise digest calculation structure.
- *
- * This initialisation is enough to safely call #digest_deinit() only.
- * To make any real digest calculation, #digest_setup_and_init() must be called.
- * @param da the digest calculation
- */
-_MHD_static_inline void
-digest_setup_zero (struct DigestAlgorithm *da)
-{
-#ifdef _DEBUG
-  da->uninitialised = false;
-  da->algo_selected = false;
-  da->ready_for_hashing = false;
-  da->hashing = false;
-#endif /* _DEBUG */
-  da->algo = MHD_DIGEST_BASE_ALGO_INVALID;
-}
-
-
-/**
- * De-initialise digest calculation structure.
- *
- * This function must be called if #digest_setup_and_init() was called for
- * @a da.
- * This function must not be called if @a da was not initialised by
- * #digest_setup_and_init() or by #digest_setup_zero().
- * @param da the digest calculation
- */
-_MHD_static_inline void
-digest_deinit (struct DigestAlgorithm *da)
-{
-  mhd_assert (! da->uninitialised);
-#ifdef MHD_MD5_HAS_DEINIT
-  if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
-    MHD_MD5_deinit (&da->ctx.md5_ctx);
-  else
-#endif /* MHD_MD5_HAS_DEINIT */
-#ifdef MHD_SHA256_HAS_DEINIT
-  if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
-    MHD_SHA256_deinit (&da->ctx.sha256_ctx);
-  else
-#endif /* MHD_SHA256_HAS_DEINIT */
-  (void) 0;
-  digest_setup_zero (da);
-}
-
-
-#else  /* ! MHD_DIGEST_HAS_DEINIT */
-#define digest_setup_zero(da) (void)0
-#define digest_deinit(da) (void)0
-#endif /* ! MHD_DIGEST_HAS_DEINIT */
-
-
-/**
- * Set-up the digest calculation structure and initialise with initial values.
- *
- * If @a da was successfully initialised, #digest_deinit() must be called
- * after finishing using of the @a da.
- *
- * This function must not be called more than once for any @a da.
- *
- * @param da the structure to set-up
- * @param algo the algorithm to use for digest calculation
- * @return boolean 'true' if successfully set-up,
- *         false otherwise.
- */
-_MHD_static_inline bool
-digest_init_one_time (struct DigestAlgorithm *da,
-                      enum MHD_DigestBaseAlgo algo)
-{
-#ifdef _DEBUG
-  da->uninitialised = false;
-  da->algo_selected = false;
-  da->ready_for_hashing = false;
-  da->hashing = false;
-#endif /* _DEBUG */
-#ifdef MHD_MD5_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_MD5 == algo)
-  {
-    da->algo = MHD_DIGEST_BASE_ALGO_MD5;
-#ifdef _DEBUG
-    da->algo_selected = true;
-#endif
-    MHD_MD5_init_one_time (&da->ctx.md5_ctx);
-#ifdef _DEBUG
-    da->ready_for_hashing = true;
-#endif
-    return true;
-  }
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA256 == algo)
-  {
-    da->algo = MHD_DIGEST_BASE_ALGO_SHA256;
-#ifdef _DEBUG
-    da->algo_selected = true;
-#endif
-    MHD_SHA256_init_one_time (&da->ctx.sha256_ctx);
-#ifdef _DEBUG
-    da->ready_for_hashing = true;
-#endif
-    return true;
-  }
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA512_256 == algo)
-  {
-    da->algo = MHD_DIGEST_BASE_ALGO_SHA512_256;
-#ifdef _DEBUG
-    da->algo_selected = true;
-#endif
-    MHD_SHA512_256_init (&da->ctx.sha512_256_ctx);
-#ifdef _DEBUG
-    da->ready_for_hashing = true;
-#endif
-    return true;
-  }
-#endif /* MHD_SHA512_256_SUPPORT */
-
-  da->algo = MHD_DIGEST_BASE_ALGO_INVALID;
-  return false; /* Unsupported or bad algorithm */
-}
-
-
-/**
- * Feed digest calculation with more data.
- * @param da the digest calculation
- * @param data the data to process
- * @param length the size of the @a data in bytes
- */
-_MHD_static_inline void
-digest_update (struct DigestAlgorithm *da,
-               const void *data,
-               size_t length)
-{
-  mhd_assert (! da->uninitialised);
-  mhd_assert (da->algo_selected);
-  mhd_assert (da->ready_for_hashing);
-#ifdef MHD_MD5_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
-    MHD_MD5_update (&da->ctx.md5_ctx, (const uint8_t *) data, length);
-  else
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
-    MHD_SHA256_update (&da->ctx.sha256_ctx, (const uint8_t *) data, length);
-  else
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
-    MHD_SHA512_256_update (&da->ctx.sha512_256_ctx,
-                           (const uint8_t *) data, length);
-  else
-#endif /* MHD_SHA512_256_SUPPORT */
-  mhd_assert (0);   /* May not happen */
-#ifdef _DEBUG
-  da->hashing = true;
-#endif
-}
-
-
-/**
- * Feed digest calculation with more data from string.
- * @param da the digest calculation
- * @param str the zero-terminated string to process
- */
-_MHD_static_inline void
-digest_update_str (struct DigestAlgorithm *da,
-                   const char *str)
-{
-  const size_t str_len = strlen (str);
-  digest_update (da, (const uint8_t *) str, str_len);
-}
-
-
-/**
- * Feed digest calculation with single colon ':' character.
- * @param da the digest calculation
- * @param str the zero-terminated string to process
- */
-_MHD_static_inline void
-digest_update_with_colon (struct DigestAlgorithm *da)
-{
-  static const uint8_t colon = (uint8_t) ':';
-  digest_update (da, &colon, 1);
-}
-
-
-/**
- * Finally calculate hash (the digest).
- * @param da the digest calculation
- * @param[out] digest the pointer to the buffer to put calculated digest,
- *                    must be at least digest_get_size(da) bytes large
- */
-_MHD_static_inline void
-digest_calc_hash (struct DigestAlgorithm *da, uint8_t *digest)
-{
-  mhd_assert (! da->uninitialised);
-  mhd_assert (da->algo_selected);
-  mhd_assert (da->ready_for_hashing);
-#ifdef MHD_MD5_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
-  {
-#ifdef MHD_MD5_HAS_FINISH
-    MHD_MD5_finish (&da->ctx.md5_ctx, digest);
-#ifdef _DEBUG
-    da->ready_for_hashing = false;
-#endif /* _DEBUG */
-#else  /* ! MHD_MD5_HAS_FINISH */
-    MHD_MD5_finish_reset (&da->ctx.md5_ctx, digest);
-#ifdef _DEBUG
-    da->ready_for_hashing = true;
-#endif /* _DEBUG */
-#endif /* ! MHD_MD5_HAS_FINISH */
-  }
-  else
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
-  {
-#ifdef MHD_SHA256_HAS_FINISH
-    MHD_SHA256_finish (&da->ctx.sha256_ctx, digest);
-#ifdef _DEBUG
-    da->ready_for_hashing = false;
-#endif /* _DEBUG */
-#else  /* ! MHD_SHA256_HAS_FINISH */
-    MHD_SHA256_finish_reset (&da->ctx.sha256_ctx, digest);
-#ifdef _DEBUG
-    da->ready_for_hashing = true;
-#endif /* _DEBUG */
-#endif /* ! MHD_SHA256_HAS_FINISH */
-  }
-  else
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
-  {
-    MHD_SHA512_256_finish (&da->ctx.sha512_256_ctx, digest);
-#ifdef _DEBUG
-    da->ready_for_hashing = false;
-#endif /* _DEBUG */
-  }
-  else
-#endif /* MHD_SHA512_256_SUPPORT */
-  mhd_assert (0);   /* Should not happen */
-#ifdef _DEBUG
-  da->hashing = false;
-#endif /* _DEBUG */
-}
-
-
-/**
- * Reset the digest calculation structure.
- *
- * @param da the structure to reset
- */
-_MHD_static_inline void
-digest_reset (struct DigestAlgorithm *da)
-{
-  mhd_assert (! da->uninitialised);
-  mhd_assert (da->algo_selected);
-  mhd_assert (! da->hashing);
-#ifdef MHD_MD5_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
-  {
-#ifdef MHD_MD5_HAS_FINISH
-    mhd_assert (! da->ready_for_hashing);
-#else  /* ! MHD_MD5_HAS_FINISH */
-    mhd_assert (da->ready_for_hashing);
-#endif /* ! MHD_MD5_HAS_FINISH */
-    MHD_MD5_reset (&da->ctx.md5_ctx);
-#ifdef _DEBUG
-    da->ready_for_hashing = true;
-#endif /* _DEBUG */
-  }
-  else
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
-  {
-#ifdef MHD_SHA256_HAS_FINISH
-    mhd_assert (! da->ready_for_hashing);
-#else  /* ! MHD_SHA256_HAS_FINISH */
-    mhd_assert (da->ready_for_hashing);
-#endif /* ! MHD_SHA256_HAS_FINISH */
-    MHD_SHA256_reset (&da->ctx.sha256_ctx);
-#ifdef _DEBUG
-    da->ready_for_hashing = true;
-#endif /* _DEBUG */
-  }
-  else
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
-  {
-    mhd_assert (! da->ready_for_hashing);
-    MHD_SHA512_256_init (&da->ctx.sha512_256_ctx);
-#ifdef _DEBUG
-    da->ready_for_hashing = true;
-#endif
-  }
-  else
-#endif /* MHD_SHA512_256_SUPPORT */
-  {
-#ifdef _DEBUG
-    da->ready_for_hashing = false;
-#endif
-    mhd_assert (0); /* May not happen, bad algorithm */
-  }
-}
-
-
-#if defined(MHD_MD5_HAS_EXT_ERROR) || defined(MHD_SHA256_HAS_EXT_ERROR)
-/**
- * Indicates that digest algorithm has external error status
- */
-#define MHD_DIGEST_HAS_EXT_ERROR 1
-#endif /* MHD_MD5_HAS_EXT_ERROR || MHD_SHA256_HAS_EXT_ERROR */
-
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-/**
- * Get external error code.
- *
- * When external digest calculation used, an error may occur during
- * initialisation or hashing data. This function checks whether external
- * error has been reported for digest calculation.
- * @param da the digest calculation
- * @return true if external error occurs
- */
-_MHD_static_inline bool
-digest_ext_error (struct DigestAlgorithm *da)
-{
-  mhd_assert (! da->uninitialised);
-  mhd_assert (da->algo_selected);
-#ifdef MHD_MD5_HAS_EXT_ERROR
-  if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
-    return 0 != da->ctx.md5_ctx.ext_error;
-#endif /* MHD_MD5_HAS_EXT_ERROR */
-#ifdef MHD_SHA256_HAS_EXT_ERROR
-  if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
-    return 0 != da->ctx.sha256_ctx.ext_error;
-#endif /* MHD_MD5_HAS_EXT_ERROR */
-  return false;
-}
-
-
-#else  /* ! MHD_DIGEST_HAS_EXT_ERROR */
-#define digest_ext_error(da) (false)
-#endif /* ! MHD_DIGEST_HAS_EXT_ERROR */
-
-
-/**
- * Extract timestamp from the given nonce.
- * @param nonce the nonce to check
- * @param noncelen the length of the nonce, zero for autodetect
- * @param[out] ptimestamp the pointer to store extracted timestamp
- * @return true if timestamp was extracted,
- *         false if nonce does not have valid timestamp.
- */
-static bool
-get_nonce_timestamp (const char *const nonce,
-                     size_t noncelen,
-                     uint64_t *const ptimestamp)
-{
-  if (0 == noncelen)
-    noncelen = strlen (nonce);
-
-  if (true
-#ifdef MHD_MD5_SUPPORT
-      && (NONCE_STD_LEN (MD5_DIGEST_SIZE) != noncelen)
-#endif /* MHD_MD5_SUPPORT */
-#if defined(MHD_SHA256_SUPPORT) || defined(MHD_SHA512_256_SUPPORT)
-      && (NONCE_STD_LEN (SHA256_SHA512_256_DIGEST_SIZE) != noncelen)
-#endif /* MHD_SHA256_SUPPORT */
-      )
-    return false;
-
-  if (TIMESTAMP_CHARS_LEN !=
-      MHD_strx_to_uint64_n_ (nonce + noncelen - TIMESTAMP_CHARS_LEN,
-                             TIMESTAMP_CHARS_LEN,
-                             ptimestamp))
-    return false;
-  return true;
-}
-
-
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
-
-/**
- * Super-fast xor-based "hash" function
- *
- * @param data the data to calculate hash for
- * @param data_size the size of the data in bytes
- * @return the "hash"
- */
-static uint32_t
-fast_simple_hash (const uint8_t *data,
-                  size_t data_size)
-{
-  uint32_t hash;
-
-  if (0 != data_size)
-  {
-    size_t i;
-    hash = data[0];
-    for (i = 1; i < data_size; i++)
-      hash = _MHD_ROTL32 (hash, 7) ^ data[i];
-  }
-  else
-    hash = 0;
-
-  return hash;
-}
-
-
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
-
-/**
- * Get index of the nonce in the nonce-nc map array.
- *
- * @param arr_size the size of nonce_nc array
- * @param nonce the pointer that referenced a zero-terminated array of nonce
- * @param noncelen the length of @a nonce, in characters
- * @return #MHD_YES if successful, #MHD_NO if invalid (or we have no NC array)
- */
-static size_t
-get_nonce_nc_idx (size_t arr_size,
-                  const char *nonce,
-                  size_t noncelen)
-{
-  mhd_assert (0 != arr_size);
-  mhd_assert (0 != noncelen);
-  return fast_simple_hash ((const uint8_t *) nonce, noncelen) % arr_size;
-}
-
-
-/**
- * Check nonce-nc map array with the new nonce counter.
- *
- * @param connection The MHD connection structure
- * @param nonce the pointer that referenced hex nonce, does not need to be
- *              zero-terminated
- * @param noncelen the length of @a nonce, in characters
- * @param nc The nonce counter
- * @return #MHD_DAUTH_NONCENC_OK if successful,
- *         #MHD_DAUTH_NONCENC_STALE if nonce is stale (or no nonce-nc array
- *         is available),
- *         #MHD_DAUTH_NONCENC_WRONG if nonce was not recodered in nonce-nc map
- *         array, while it should.
- */
-static enum MHD_CheckNonceNC_
-check_nonce_nc (struct MHD_Connection *connection,
-                const char *nonce,
-                size_t noncelen,
-                uint64_t nonce_time,
-                uint64_t nc)
-{
-  struct MHD_Daemon *daemon = MHD_get_master (connection->daemon);
-  struct MHD_NonceNc *nn;
-  uint32_t mod;
-  enum MHD_CheckNonceNC_ ret;
-
-  mhd_assert (0 != noncelen);
-  mhd_assert (0 != nc);
-  if (MAX_DIGEST_NONCE_LENGTH < noncelen)
-    return MHD_CHECK_NONCENC_WRONG; /* This should be impossible, but static analysis
-                      tools have a hard time with it *and* this also
-                      protects against unsafe modifications that may
-                      happen in the future... */
-  mod = daemon->nonce_nc_size;
-  if (0 == mod)
-    return MHD_CHECK_NONCENC_STALE;  /* no array! */
-  if (nc >= UINT32_MAX - 64)
-    return MHD_CHECK_NONCENC_STALE;  /* Overflow, unrealistically high value */
-
-  nn = &daemon->nnc[get_nonce_nc_idx (mod, nonce, noncelen)];
-
-  MHD_mutex_lock_chk_ (&daemon->nnc_lock);
-
-  mhd_assert (0 == nn->nonce[noncelen]); /* The old value must be valid */
-
-  if ( (0 != memcmp (nn->nonce, nonce, noncelen)) ||
-       (0 != nn->nonce[noncelen]) )
-  { /* The nonce in the slot does not match nonce from the client */
-    if (0 == nn->nonce[0])
-    { /* The slot was never used, while the client's nonce value should be
-       * recorded when it was generated by MHD */
-      ret = MHD_CHECK_NONCENC_WRONG;
-    }
-    else if (0 != nn->nonce[noncelen])
-    { /* The value is the slot is wrong */
-      ret =  MHD_CHECK_NONCENC_STALE;
-    }
-    else
-    {
-      uint64_t slot_ts; /**< The timestamp in the slot */
-      if (! get_nonce_timestamp (nn->nonce, noncelen, &slot_ts))
-      {
-        mhd_assert (0); /* The value is the slot is wrong */
-        ret = MHD_CHECK_NONCENC_STALE;
-      }
-      else
-      {
-        /* Unsigned value, will be large if nonce_time is less than slot_ts */
-        const uint64_t ts_diff = TRIM_TO_TIMESTAMP (nonce_time - slot_ts);
-        if ((REUSE_TIMEOUT * 1000) >= ts_diff)
-        {
-          /* The nonce from the client may not have been placed in the slot
-           * because another nonce in that slot has not yet expired. */
-          ret = MHD_CHECK_NONCENC_STALE;
-        }
-        else if (TRIM_TO_TIMESTAMP (UINT64_MAX) / 2 >= ts_diff)
-        {
-          /* Too large value means that nonce_time is less than slot_ts.
-           * The nonce from the client may have been overwritten by the newer
-           * nonce. */
-          ret = MHD_CHECK_NONCENC_STALE;
-        }
-        else
-        {
-          /* The nonce from the client should be generated after the nonce
-           * in the slot has been expired, the nonce must be recorded, but
-           * it's not. */
-          ret = MHD_CHECK_NONCENC_WRONG;
-        }
-      }
-    }
-  }
-  else if (nc > nn->nc)
-  {
-    /* 'nc' is larger, shift bitmask and bump limit */
-    const uint32_t jump_size = (uint32_t) nc - nn->nc;
-    if (64 > jump_size)
-    {
-      /* small jump, less than mask width */
-      nn->nmask <<= jump_size;
-      /* Set bit for the old 'nc' value */
-      nn->nmask |= (UINT64_C (1) << (jump_size - 1));
-    }
-    else if (64 == jump_size)
-      nn->nmask = (UINT64_C (1) << 63);
-    else
-      nn->nmask = 0;                /* big jump, unset all bits in the mask */
-    nn->nc = (uint32_t) nc;
-    ret = MHD_CHECK_NONCENC_OK;
-  }
-  else if (nc < nn->nc)
-  {
-    /* Note that we use 64 here, as we do not store the
-       bit for 'nn->nc' itself in 'nn->nmask' */
-    if ( (nc + 64 >= nn->nc) &&
-         (0 == ((UINT64_C (1) << (nn->nc - nc - 1)) & nn->nmask)) )
-    {
-      /* Out-of-order nonce, but within 64-bit bitmask, set bit */
-      nn->nmask |= (UINT64_C (1) << (nn->nc - nc - 1));
-      ret = MHD_CHECK_NONCENC_OK;
-    }
-    else
-      /* 'nc' was already used or too old (more then 64 values ago) */
-      ret = MHD_CHECK_NONCENC_STALE;
-  }
-  else /* if (nc == nn->nc) */
-    /* 'nc' was already used */
-    ret = MHD_CHECK_NONCENC_STALE;
-
-  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
-
-  return ret;
-}
-
-
-/**
- * Get username type used by the client.
- * This function does not check whether userhash can be decoded or
- * extended notation (if used) is valid.
- * @param params the Digest Authorization parameters
- * @return the type of username
- */
-_MHD_static_inline enum MHD_DigestAuthUsernameType
-get_rq_uname_type (const struct MHD_RqDAuth *params)
-{
-  if (NULL != params->username.value.str)
-  {
-    if (NULL == params->username_ext.value.str)
-      return params->userhash ?
-             MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH :
-             MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD;
-    else  /* Both 'username' and 'username*' are used */
-      return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
-  }
-  else if (NULL != params->username_ext.value.str)
-  {
-    if (! params->username_ext.quoted && ! params->userhash &&
-        (MHD_DAUTH_EXT_PARAM_MIN_LEN <= params->username_ext.value.len) )
-      return MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED;
-    else
-      return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
-  }
-
-  return MHD_DIGEST_AUTH_UNAME_TYPE_MISSING;
-}
-
-
-/**
- * Get total size required for 'username' and 'userhash_bin'
- * @param params the Digest Authorization parameters
- * @param uname_type the type of username
- * @return the total size required for 'username' and
- *         'userhash_bin' is userhash is used
- */
-_MHD_static_inline size_t
-get_rq_unames_size (const struct MHD_RqDAuth *params,
-                    enum MHD_DigestAuthUsernameType uname_type)
-{
-  size_t s;
-
-  mhd_assert (get_rq_uname_type (params) == uname_type);
-  s = 0;
-  if ((MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type) ||
-      (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type) )
-  {
-    s += params->username.value.len + 1; /* Add one byte for zero-termination */
-    if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
-      s += (params->username.value.len + 1) / 2;
-  }
-  else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type)
-    s += params->username_ext.value.len
-         - MHD_DAUTH_EXT_PARAM_MIN_LEN + 1; /* Add one byte for zero-termination */
-  return s;
-}
-
-
-/**
- * Get unquoted version of Digest Authorization parameter.
- * This function automatically zero-teminate the result.
- * @param param the parameter to extract
- * @param[out] buf the output buffer, must be enough size to hold the result,
- *                 the recommended size is 'param->value.len + 1'
- * @return the size of the result, not including the terminating zero
- */
-static size_t
-get_rq_param_unquoted_copy_z (const struct MHD_RqDAuthParam *param, char *buf)
-{
-  size_t len;
-  mhd_assert (NULL != param->value.str);
-  if (! param->quoted)
-  {
-    memcpy (buf, param->value.str, param->value.len);
-    buf [param->value.len] = 0;
-    return param->value.len;
-  }
-
-  len = MHD_str_unquote (param->value.str, param->value.len, buf);
-  mhd_assert (0 != len);
-  mhd_assert (len < param->value.len);
-  buf[len] = 0;
-  return len;
-}
-
-
-/**
- * Get decoded version of username from extended notation.
- * This function automatically zero-teminate the result.
- * @param uname_ext the string of client's 'username*' parameter value
- * @param uname_ext_len the length of @a uname_ext in chars
- * @param[out] buf the output buffer to put decoded username value
- * @param buf_size the size of @a buf
- * @return the number of characters copied to the output buffer or
- *         -1 if wrong extended notation is used.
- */
-static ssize_t
-get_rq_extended_uname_copy_z (const char *uname_ext, size_t uname_ext_len,
-                              char *buf, size_t buf_size)
-{
-  size_t r;
-  size_t w;
-  if ((size_t) SSIZE_MAX < uname_ext_len)
-    return -1; /* Too long input string */
-
-  if (MHD_DAUTH_EXT_PARAM_MIN_LEN > uname_ext_len)
-    return -1; /* Required prefix is missing */
-
-  if (! MHD_str_equal_caseless_bin_n_ (uname_ext, MHD_DAUTH_EXT_PARAM_PREFIX,
-                                       MHD_STATICSTR_LEN_ ( \
-                                         MHD_DAUTH_EXT_PARAM_PREFIX)))
-    return -1; /* Only UTF-8 is supported, as it is implied by RFC 7616 */
-
-  r = MHD_STATICSTR_LEN_ (MHD_DAUTH_EXT_PARAM_PREFIX);
-  /* Skip language tag */
-  while (r < uname_ext_len && '\'' != uname_ext[r])
-  {
-    const char chr = uname_ext[r];
-    if ((' ' == chr) || ('\t' == chr) || ('\"' == chr) || (',' == chr) ||
-        (';' == chr) )
-      return -1; /* Wrong char in language tag */
-    r++;
-  }
-  if (r >= uname_ext_len)
-    return -1; /* The end of the language tag was not found */
-  r++; /* Advance to the next char */
-
-  w = MHD_str_pct_decode_strict_n_ (uname_ext + r, uname_ext_len - r,
-                                    buf, buf_size);
-  if ((0 == w) && (0 != uname_ext_len - r))
-    return -1; /* Broken percent encoding */
-  buf[w] = 0; /* Zero terminate the result */
-  mhd_assert (SSIZE_MAX > w);
-  return (ssize_t) w;
-}
-
-
-/**
- * Get copy of username used by the client.
- * @param params the Digest Authorization parameters
- * @param uname_type the type of username
- * @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
- */
-static size_t
-get_rq_uname (const struct MHD_RqDAuth *params,
-              enum MHD_DigestAuthUsernameType uname_type,
-              struct MHD_DigestAuthUsernameInfo *uname_info,
-              uint8_t *buf,
-              size_t buf_size)
-{
-  size_t buf_used;
-
-  buf_used = 0;
-  mhd_assert (get_rq_uname_type (params) == uname_type);
-  mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type);
-  mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type);
-
-  uname_info->username = NULL;
-  uname_info->username_len = 0;
-  uname_info->userhash_hex = NULL;
-  uname_info->userhash_hex_len = 0;
-  uname_info->userhash_bin = NULL;
-
-  if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type)
-  {
-    uname_info->username = (char *) (buf + buf_used);
-    uname_info->username_len =
-      get_rq_param_unquoted_copy_z (&params->username,
-                                    uname_info->username);
-    buf_used += uname_info->username_len + 1;
-    uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD;
-  }
-  else if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
-  {
-    size_t res;
-
-    uname_info->userhash_hex = (char *) (buf + buf_used);
-    uname_info->userhash_hex_len =
-      get_rq_param_unquoted_copy_z (&params->username,
-                                    uname_info->userhash_hex);
-    buf_used += uname_info->userhash_hex_len + 1;
-    uname_info->userhash_bin = (uint8_t *) (buf + buf_used);
-    res = MHD_hex_to_bin (uname_info->userhash_hex,
-                          uname_info->userhash_hex_len,
-                          uname_info->userhash_bin);
-    if (res != uname_info->userhash_hex_len / 2)
-    {
-      uname_info->userhash_bin = NULL;
-      uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
-    }
-    else
-    {
-      /* Avoid pointers outside allocated region when the size is zero */
-      if (0 == res)
-        uname_info->userhash_bin = (uint8_t *) uname_info->username;
-      uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH;
-      buf_used += res;
-    }
-  }
-  else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type)
-  {
-    ssize_t res;
-    res = get_rq_extended_uname_copy_z (params->username_ext.value.str,
-                                        params->username_ext.value.len,
-                                        (char *) (buf + buf_used),
-                                        buf_size - buf_used);
-    if (0 > res)
-      uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
-    else
-    {
-      uname_info->username = (char *) (buf + buf_used);
-      uname_info->username_len = (size_t) res;
-      uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED;
-      buf_used += uname_info->username_len + 1;
-    }
-  }
-  else
-  {
-    mhd_assert (0);
-    uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
-  }
-  mhd_assert (buf_size >= buf_used);
-  return buf_used;
-}
-
-
-/**
- * Result of request's Digest Authorization 'nc' value extraction
- */
-enum MHD_GetRqNCResult
-{
-  MHD_GET_RQ_NC_NONE = -1,    /**< No 'nc' value */
-  MHD_GET_RQ_NC_VALID = 0,    /**< Readable 'nc' value */
-  MHD_GET_RQ_NC_TOO_LONG = 1, /**< The 'nc' value is too long */
-  MHD_GET_RQ_NC_TOO_LARGE = 2,/**< The 'nc' value is too big to fit uint32_t */
-  MHD_GET_RQ_NC_BROKEN = 3    /**< The 'nc' value is not a number */
-};
-
-
-/**
- * Get 'nc' value from request's Authorization header
- * @param params the request digest authentication
- * @param[out] nc the pointer to put nc value to
- * @return enum value indicating the result
- */
-static enum MHD_GetRqNCResult
-get_rq_nc (const struct MHD_RqDAuth *params,
-           uint32_t *nc)
-{
-  const struct MHD_RqDAuthParam *const nc_param =
-    &params->nc;
-  char unq[16];
-  const char *val;
-  size_t val_len;
-  size_t res;
-  uint64_t nc_val;
-
-  if (NULL == nc_param->value.str)
-    return MHD_GET_RQ_NC_NONE;
-
-  if (0 == nc_param->value.len)
-    return MHD_GET_RQ_NC_BROKEN;
-
-  if (! nc_param->quoted)
-  {
-    val = nc_param->value.str;
-    val_len = nc_param->value.len;
-  }
-  else
-  {
-    /* Actually no backslashes must be used in 'nc' */
-    if (sizeof(unq) < params->nc.value.len)
-      return MHD_GET_RQ_NC_TOO_LONG;
-    val_len = MHD_str_unquote (nc_param->value.str, nc_param->value.len, unq);
-    if (0 == val_len)
-      return MHD_GET_RQ_NC_BROKEN;
-    val = unq;
-  }
-
-  res = MHD_strx_to_uint64_n_ (val, val_len, &nc_val);
-  if (0 == res)
-  {
-    const char f = val[0];
-    if ( (('9' >= f) && ('0' <= f)) ||
-         (('F' >= f) && ('A' <= f)) ||
-         (('a' <= f) && ('f' >= f)) )
-      return MHD_GET_RQ_NC_TOO_LARGE;
-    else
-      return MHD_GET_RQ_NC_BROKEN;
-  }
-  if (val_len != res)
-    return MHD_GET_RQ_NC_BROKEN;
-  if (UINT32_MAX < nc_val)
-    return MHD_GET_RQ_NC_TOO_LARGE;
-  *nc = (uint32_t) nc_val;
-  return MHD_GET_RQ_NC_VALID;
-}
-
-
-/**
- * Get information about Digest Authorization client's header.
- *
- * @param connection The MHD connection structure
- * @return NULL no valid Digest Authorization header is used in the request;
- *         a pointer structure with information if the valid request header
- *         found, free using #MHD_free().
- * @note Available since #MHD_VERSION 0x00097701
- * @ingroup authentication
- */
-_MHD_EXTERN struct MHD_DigestAuthInfo *
-MHD_digest_auth_get_request_info3 (struct MHD_Connection *connection)
-{
-  const struct MHD_RqDAuth *params;
-  struct MHD_DigestAuthInfo *info;
-  enum MHD_DigestAuthUsernameType uname_type;
-  size_t unif_buf_size;
-  uint8_t *unif_buf_ptr;
-  size_t unif_buf_used;
-  enum MHD_GetRqNCResult nc_res;
-
-  params = MHD_get_rq_dauth_params_ (connection);
-  if (NULL == params)
-    return NULL;
-
-  unif_buf_size = 0;
-
-  uname_type = get_rq_uname_type (params);
-
-  unif_buf_size += get_rq_unames_size (params, uname_type);
-
-  if (NULL != params->opaque.value.str)
-    unif_buf_size += params->opaque.value.len + 1;  /* Add one for zero-termination */
-  if (NULL != params->realm.value.str)
-    unif_buf_size += params->realm.value.len + 1;   /* Add one for zero-termination */
-  info = (struct MHD_DigestAuthInfo *)
-         MHD_calloc_ (1, (sizeof(struct MHD_DigestAuthInfo)) + unif_buf_size);
-  unif_buf_ptr = (uint8_t *) (info + 1);
-  unif_buf_used = 0;
-
-  info->algo3 = params->algo3;
-
-  if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type) &&
-       (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type) )
-    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;
-
-  if (NULL != params->opaque.value.str)
-  {
-    info->opaque = (char *) (unif_buf_ptr + unif_buf_used);
-    info->opaque_len = get_rq_param_unquoted_copy_z (&params->opaque,
-                                                     info->opaque);
-    unif_buf_used += info->opaque_len + 1;
-  }
-  if (NULL != params->realm.value.str)
-  {
-    info->realm = (char *) (unif_buf_ptr + unif_buf_used);
-    info->realm_len = get_rq_param_unquoted_copy_z (&params->realm,
-                                                    info->realm);
-    unif_buf_used += info->realm_len + 1;
-  }
-
-  mhd_assert (unif_buf_size >= unif_buf_used);
-
-  info->qop = params->qop;
-
-  if (NULL != params->cnonce.value.str)
-    info->cnonce_len = params->cnonce.value.len;
-  else
-    info->cnonce_len = 0;
-
-  nc_res = get_rq_nc (params, &info->nc);
-  if (MHD_GET_RQ_NC_VALID != nc_res)
-    info->nc = MHD_DIGEST_AUTH_INVALID_NC_VALUE;
-
-  return info;
-}
-
-
-/**
- * Get the username from Digest Authorization client's header.
- *
- * @param connection The MHD connection structure
- * @return NULL if no valid Digest Authorization header is used in the request,
- *         or no username parameter is present in the header, or username is
- *         provided incorrectly by client (see description for
- *         #MHD_DIGEST_AUTH_UNAME_TYPE_INVALID);
- *         a pointer structure with information if the valid request header
- *         found, free using #MHD_free().
- * @sa MHD_digest_auth_get_request_info3() provides more complete information
- * @note Available since #MHD_VERSION 0x00097701
- * @ingroup authentication
- */
-_MHD_EXTERN struct MHD_DigestAuthUsernameInfo *
-MHD_digest_auth_get_username3 (struct MHD_Connection *connection)
-{
-  const struct MHD_RqDAuth *params;
-  struct MHD_DigestAuthUsernameInfo *uname_info;
-  enum MHD_DigestAuthUsernameType uname_type;
-  size_t unif_buf_size;
-  uint8_t *unif_buf_ptr;
-  size_t unif_buf_used;
-
-  params = MHD_get_rq_dauth_params_ (connection);
-  if (NULL == params)
-    return NULL;
-
-  uname_type = get_rq_uname_type (params);
-  if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING == uname_type) ||
-       (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_type) )
-    return NULL;
-
-  unif_buf_size = get_rq_unames_size (params, uname_type);
-
-  uname_info = (struct MHD_DigestAuthUsernameInfo *)
-               MHD_calloc_ (1, (sizeof(struct MHD_DigestAuthUsernameInfo))
-                            + unif_buf_size);
-  unif_buf_ptr = (uint8_t *) (uname_info + 1);
-  unif_buf_used = get_rq_uname (params, uname_type, uname_info, unif_buf_ptr,
-                                unif_buf_size);
-  mhd_assert (unif_buf_size >= unif_buf_used);
-  (void) unif_buf_used; /* Mute compiler warning on non-debug builds */
-  mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_info->uname_type);
-
-  if (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_info->uname_type)
-  {
-    free (uname_info);
-    return NULL;
-  }
-  mhd_assert (uname_type == uname_info->uname_type);
-  uname_info->algo3 = params->algo3;
-
-  return uname_info;
-}
-
-
-/**
- * Get the username from the authorization header sent by the client
- *
- * This function supports username in standard and extended notations.
- * "userhash" is not supported by this function.
- *
- * @param connection The MHD connection structure
- * @return NULL if no username could be found, username provided as
- *         "userhash", extended notation broken or memory allocation error
- *         occurs;
- *         a pointer to the username if found, free using #MHD_free().
- * @warning Returned value must be freed by #MHD_free().
- * @sa #MHD_digest_auth_get_username3()
- * @ingroup authentication
- */
-_MHD_EXTERN char *
-MHD_digest_auth_get_username (struct MHD_Connection *connection)
-{
-  const struct MHD_RqDAuth *params;
-  char *username;
-  size_t buf_size;
-  enum MHD_DigestAuthUsernameType uname_type;
-
-  params = MHD_get_rq_dauth_params_ (connection);
-  if (NULL == params)
-    return NULL;
-
-  uname_type = get_rq_uname_type (params);
-
-  if ( (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD != uname_type) &&
-       (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != uname_type) )
-    return NULL;
-
-  buf_size = get_rq_unames_size (params, uname_type);
-
-  mhd_assert (0 != buf_size);
-
-  username = (char *) MHD_calloc_ (1, buf_size);
-  if (NULL == username)
-    return NULL;
-
-  if (1)
-  {
-    struct MHD_DigestAuthUsernameInfo uname_strct;
-    size_t used;
-
-    memset (&uname_strct, 0, sizeof(uname_strct));
-
-    used = get_rq_uname (params, uname_type, &uname_strct,
-                         (uint8_t *) username, buf_size);
-    if (uname_type != uname_strct.uname_type)
-    { /* Broken encoding for extended notation */
-      free (username);
-      return NULL;
-    }
-    (void) used; /* Mute compiler warning for non-debug builds */
-    mhd_assert (buf_size >= used);
-  }
-
-  return username;
-}
-
-
-/**
- * Calculate the server nonce so that it mitigates replay attacks
- * The current format of the nonce is ...
- * 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
- * @param method HTTP method as a string
- * @param rnd the pointer to a character array for the random seed
- * @param rnd_size The size of the random seed array @a rnd
- * @param saddr the pointer to the socket address structure
- * @param saddr_size the size of the socket address structure @a saddr
- * @param uri the HTTP URI (in MHD, without the arguments ("?k=v")
- * @param uri_len the length of the @a uri
- * @param first_header the pointer to the first request's header
- * @param realm A string of characters that describes the realm of auth.
- * @param realm_len the length of the @a realm.
- * @param bind_options the nonce bind options (#MHD_DAuthBindNonce values).
- * @param da digest algorithm to use
- * @param[out] nonce the pointer to a character array for the nonce to put in,
- *                   must provide NONCE_STD_LEN(digest_get_size(da)) bytes,
- *                   result is NOT zero-terminated
- */
-static void
-calculate_nonce (uint64_t nonce_time,
-                 enum MHD_HTTP_Method mthd_e,
-                 const char *method,
-                 const char *rnd,
-                 size_t rnd_size,
-                 const struct sockaddr_storage *saddr,
-                 size_t saddr_size,
-                 const char *uri,
-                 size_t uri_len,
-                 const struct MHD_HTTP_Req_Header *first_header,
-                 const char *realm,
-                 size_t realm_len,
-                 unsigned int bind_options,
-                 struct DigestAlgorithm *da,
-                 char *nonce)
-{
-  mhd_assert (! da->hashing);
-  if (1)
-  {
-    /* Add the timestamp to the hash calculation */
-    uint8_t timestamp[TIMESTAMP_BIN_SIZE];
-    /* If the nonce_time is milliseconds, then the same 48 bit value will repeat
-     * every 8 919 years, which is more than enough to mitigate a replay attack */
-#if TIMESTAMP_BIN_SIZE != 6
-#error The code needs to be updated here
-#endif
-    timestamp[0] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 0)));
-    timestamp[1] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 1)));
-    timestamp[2] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 2)));
-    timestamp[3] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 3)));
-    timestamp[4] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 4)));
-    timestamp[5] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 5)));
-    MHD_bin_to_hex (timestamp,
-                    sizeof (timestamp),
-                    nonce + digest_get_size (da) * 2);
-    digest_update (da,
-                   timestamp,
-                   sizeof (timestamp));
-  }
-  if (rnd_size > 0)
-  {
-    /* Add the unique random value to the hash calculation */
-    digest_update_with_colon (da);
-    digest_update (da,
-                   rnd,
-                   rnd_size);
-  }
-  if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) &&
-       (0 != saddr_size) )
-  {
-    /* Add full client address including source port to make unique nonces
-     * for requests received exactly at the same time */
-    digest_update_with_colon (da);
-    digest_update (da,
-                   saddr,
-                   saddr_size);
-  }
-  if ( (0 != (bind_options & MHD_DAUTH_BIND_NONCE_CLIENT_IP)) &&
-       (0 != saddr_size) )
-  {
-    /* Add the client's IP address to the hash calculation */
-    digest_update_with_colon (da);
-    if (AF_INET == saddr->ss_family)
-      digest_update (da,
-                     &((const struct sockaddr_in *) saddr)->sin_addr,
-                     sizeof(((const struct sockaddr_in *) saddr)->sin_addr));
-#ifdef HAVE_INET6
-    else if (AF_INET6 == saddr->ss_family)
-      digest_update (da,
-                     &((const struct sockaddr_in6 *) saddr)->sin6_addr,
-                     sizeof(((const struct sockaddr_in6 *) saddr)->sin6_addr));
-#endif /* HAVE_INET6 */
-  }
-  if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) ||
-       (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI)))
-  {
-    /* Add the request method to the hash calculation */
-    digest_update_with_colon (da);
-    if (MHD_HTTP_MTHD_OTHER != mthd_e)
-    {
-      uint8_t mthd_for_hash;
-      if (MHD_HTTP_MTHD_HEAD != mthd_e)
-        mthd_for_hash = (uint8_t) mthd_e;
-      else /* Treat HEAD method in the same way as GET method */
-        mthd_for_hash = (uint8_t) MHD_HTTP_MTHD_GET;
-      digest_update (da,
-                     &mthd_for_hash,
-                     sizeof(mthd_for_hash));
-    }
-    else
-      digest_update_str (da, method);
-  }
-
-  if (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI))
-  {
-    /* Add the request URI to the hash calculation */
-    digest_update_with_colon (da);
-
-    digest_update (da,
-                   uri,
-                   uri_len);
-  }
-  if (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI_PARAMS))
-  {
-    /* Add the request URI parameters to the hash calculation */
-    const struct MHD_HTTP_Req_Header *h;
-
-    digest_update_with_colon (da);
-    for (h = first_header; NULL != h; h = h->next)
-    {
-      if (MHD_GET_ARGUMENT_KIND != h->kind)
-        continue;
-      digest_update (da, "\0", 2);
-      if (0 != h->header_size)
-        digest_update (da, h->header, h->header_size);
-      digest_update (da, "", 1);
-      if (0 != h->value_size)
-        digest_update (da, h->value, h->value_size);
-    }
-  }
-  if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) ||
-       (0 != (bind_options & MHD_DAUTH_BIND_NONCE_REALM)))
-  {
-    /* Add the realm to the hash calculation */
-    digest_update_with_colon (da);
-    digest_update (da,
-                   realm,
-                   realm_len);
-  }
-  if (1)
-  {
-    uint8_t hash[MAX_DIGEST];
-    digest_calc_hash (da, hash);
-    MHD_bin_to_hex (hash,
-                    digest_get_size (da),
-                    nonce);
-  }
-}
-
-
-/**
- * Check whether it is possible to use slot in nonce-nc map array.
- *
- * Should be called with mutex held to avoid external modification of
- * the slot data.
- *
- * @param nn the pointer to the nonce-nc slot
- * @param now the current time
- * @param new_nonce the new nonce supposed to be stored in this slot,
- *                  zero-terminated
- * @param new_nonce_len the length of the @a new_nonce in chars, not including
- *                      the terminating zero.
- * @return true if the slot can be used to store the new nonce,
- *         false otherwise.
- */
-static bool
-is_slot_available (const struct MHD_NonceNc *const nn,
-                   const uint64_t now,
-                   const char *const new_nonce,
-                   size_t new_nonce_len)
-{
-  uint64_t timestamp;
-  bool timestamp_valid;
-  mhd_assert (new_nonce_len <= NONCE_STD_LEN (MAX_DIGEST));
-  mhd_assert (NONCE_STD_LEN (MAX_DIGEST) <= MAX_DIGEST_NONCE_LENGTH);
-  if (0 == nn->nonce[0])
-    return true; /* The slot is empty */
-
-  if (0 == memcmp (nn->nonce, new_nonce, new_nonce_len))
-  {
-    /* The slot has the same nonce already. This nonce cannot be registered
-     * again as it would just clear 'nc' usage history. */
-    return false;
-  }
-
-  if (0 != nn->nc)
-    return true; /* Client already used the nonce in this slot at least
-                    one time, re-use the slot */
-
-  /* The nonce must be zero-terminated */
-  mhd_assert (0 == nn->nonce[sizeof(nn->nonce) - 1]);
-  if (0 != nn->nonce[sizeof(nn->nonce) - 1])
-    return true; /* Wrong nonce format in the slot */
-
-  timestamp_valid = get_nonce_timestamp (nn->nonce, 0, &timestamp);
-  mhd_assert (timestamp_valid);
-  if (! timestamp_valid)
-    return true; /* Invalid timestamp in nonce-nc, should not be possible */
-
-  if ((REUSE_TIMEOUT * 1000) < TRIM_TO_TIMESTAMP (now - timestamp))
-    return true;
-
-  return false;
-}
-
-
-/**
- * Calculate the server nonce so that it mitigates replay attacks and add
- * the new nonce to the nonce-nc map array.
- *
- * @param connection the MHD connection structure
- * @param timestamp the current timestamp
- * @param realm the string of characters that describes the realm of auth
- * @param realm_len the length of the @a realm
- * @param da the digest algorithm to use
- * @param[out] nonce the pointer to a character array for the nonce to put in,
- *                   must provide NONCE_STD_LEN(digest_get_size(da)) bytes,
- *                   result is NOT zero-terminated
- * @return true if the new nonce has been added to the nonce-nc map array,
- *         false otherwise.
- */
-static bool
-calculate_add_nonce (struct MHD_Connection *const connection,
-                     uint64_t timestamp,
-                     const char *realm,
-                     size_t realm_len,
-                     struct DigestAlgorithm *da,
-                     char *nonce)
-{
-  struct MHD_Daemon *const daemon = MHD_get_master (connection->daemon);
-  struct MHD_NonceNc *nn;
-  const size_t nonce_size = NONCE_STD_LEN (digest_get_size (da));
-  bool ret;
-
-  mhd_assert (! da->hashing);
-  mhd_assert (MAX_DIGEST_NONCE_LENGTH >= nonce_size);
-  mhd_assert (0 != nonce_size);
-
-  calculate_nonce (timestamp,
-                   connection->rq.http_mthd,
-                   connection->rq.method,
-                   daemon->digest_auth_random,
-                   daemon->digest_auth_rand_size,
-                   connection->addr,
-                   (size_t) connection->addr_len,
-                   connection->rq.url,
-                   connection->rq.url_len,
-                   connection->rq.headers_received,
-                   realm,
-                   realm_len,
-                   daemon->dauth_bind_type,
-                   da,
-                   nonce);
-
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-  if (digest_ext_error (da))
-    return false;
-#endif /* MHD_DIGEST_HAS_EXT_ERROR */
-
-  if (0 == daemon->nonce_nc_size)
-    return false;
-
-  /* Sanity check for values */
-  mhd_assert (MAX_DIGEST_NONCE_LENGTH == NONCE_STD_LEN (MAX_DIGEST));
-
-  nn = daemon->nnc + get_nonce_nc_idx (daemon->nonce_nc_size,
-                                       nonce,
-                                       nonce_size);
-
-  MHD_mutex_lock_chk_ (&daemon->nnc_lock);
-  if (is_slot_available (nn, timestamp, nonce, nonce_size))
-  {
-    memcpy (nn->nonce,
-            nonce,
-            nonce_size);
-    nn->nonce[nonce_size] = 0;  /* With terminating zero */
-    nn->nc = 0;
-    nn->nmask = 0;
-    ret = true;
-  }
-  else
-    ret = false;
-  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
-
-  return ret;
-}
-
-
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
-
-/**
- * Calculate the server nonce so that it mitigates replay attacks and add
- * the new nonce to the nonce-nc map array.
- *
- * @param connection the MHD connection structure
- * @param realm A string of characters that describes the realm of auth.
- * @param da digest algorithm to use
- * @param[out] nonce the pointer to a character array for the nonce to put in,
- *                   must provide NONCE_STD_LEN(digest_get_size(da)) bytes,
- *                   result is NOT zero-terminated
- */
-static bool
-calculate_add_nonce_with_retry (struct MHD_Connection *const connection,
-                                const char *realm,
-                                struct DigestAlgorithm *da,
-                                char *nonce)
-{
-  const uint64_t timestamp1 = MHD_monotonic_msec_counter ();
-  const size_t realm_len = strlen (realm);
-  mhd_assert (! da->hashing);
-
-#ifdef HAVE_MESSAGES
-  if (0 == MHD_get_master (connection->daemon)->digest_auth_rand_size)
-    MHD_DLOG (connection->daemon,
-              _ ("Random value was not initialised by " \
-                 "MHD_OPTION_DIGEST_AUTH_RANDOM or " \
-                 "MHD_OPTION_DIGEST_AUTH_RANDOM_COPY, generated nonces " \
-                 "are predictable.\n"));
-#endif
-
-  if (! calculate_add_nonce (connection, timestamp1, realm, realm_len, da,
-                             nonce))
-  {
-    /* Either:
-     * 1. The same nonce was already generated. If it will be used then one
-     * of the clients will fail (as no initial 'nc' value could be given to
-     * the client, the second client which will use 'nc=00000001' will fail).
-     * 2. Another nonce uses the same slot, and this nonce never has been
-     * used by the client and this nonce is still fresh enough.
-     */
-    const size_t digest_size = digest_get_size (da);
-    char nonce2[NONCE_STD_LEN (MAX_DIGEST) + 1];
-    uint64_t timestamp2;
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-    if (digest_ext_error (da))
-      return false; /* No need to re-try */
-#endif /* MHD_DIGEST_HAS_EXT_ERROR */
-    if (0 == MHD_get_master (connection->daemon)->nonce_nc_size)
-      return false; /* No need to re-try */
-
-    timestamp2 = MHD_monotonic_msec_counter ();
-    if (timestamp1 == timestamp2)
-    {
-      /* The timestamps are equal, need to generate some arbitrary
-       * difference for nonce. */
-      /* As the number is needed only to differentiate clients, weak
-       * pseudo-random generators could be used. Seeding is not needed. */
-      uint64_t base1;
-      uint32_t base2;
-      uint16_t base3;
-      uint8_t base4;
-#ifdef HAVE_RANDOM
-      base1 = ((uint64_t) random ()) ^ UINT64_C (0x54a5acff5be47e63);
-      base4 = 0xb8;
-#elif defined(HAVE_RAND)
-      base1 = ((uint64_t) rand ()) ^ UINT64_C (0xc4bcf553b12f3965);
-      base4 = 0x92;
-#else
-      /* Monotonic msec counter alone does not really help here as it is already
-         known that this value is not unique. */
-      base1 = ((uint64_t) (uintptr_t) nonce2) ^ UINT64_C (0xf2e1b21bc6c92655);
-      base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1);
-      base2 = _MHD_ROTR32 (base2, 4);
-      base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2);
-      base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3);
-      base1 = ((uint64_t) MHD_monotonic_msec_counter ())
-              ^ UINT64_C (0xccab93f72cf5b15);
-#endif
-      base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1);
-      base2 = _MHD_ROTL32 (base2, (((base4 >> 4) ^ base4) % 32));
-      base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2);
-      base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3);
-      /* Use up to 127 ms difference */
-      timestamp2 -= (base4 & DAUTH_JUMPBACK_MAX);
-      if (timestamp1 == timestamp2)
-        timestamp2 -= 2; /* Fallback value */
-    }
-    digest_reset (da);
-    if (! calculate_add_nonce (connection, timestamp2, realm, realm_len, da,
-                               nonce2))
-    {
-      /* No free slot has been found. Re-tries are expensive, just use
-       * the generated nonce. As it is not stored in nonce-nc map array,
-       * the next request of the client will be recognized as valid, but 'stale'
-       * so client should re-try automatically. */
-      return false;
-    }
-    memcpy (nonce, nonce2, NONCE_STD_LEN (digest_size));
-  }
-  return true;
-}
-
-
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
-
-/**
- * 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)
-{
-  mhd_assert (! da->hashing);
-  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 a 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
- * @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 bin_buf_size 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 (or external error has occurred,
- *         see #MHD_FEATURE_EXTERN_HASH).
- * @sa #MHD_digest_auth_check_digest3()
- * @note Available since #MHD_VERSION 0x00097701
- * @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;
-  enum MHD_Result ret;
-  if (! digest_init_one_time (&da, get_base_digest_algo (algo3)))
-    return MHD_NO;
-
-  if (digest_get_size (&da) > bin_buf_size)
-    ret = MHD_NO;
-  else
-  {
-    calc_userdigest (&da,
-                     username,
-                     strlen (username),
-                     realm,
-                     strlen (realm),
-                     password,
-                     userdigest_bin);
-    ret = MHD_YES;
-
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-    if (digest_ext_error (&da))
-      ret = MHD_NO;
-#endif /* MHD_DIGEST_HAS_EXT_ERROR */
-  }
-  digest_deinit (&da);
-
-  return ret;
-}
-
-
-/**
- * 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);
-  mhd_assert (! da->hashing);
-  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 by 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 (or external error has occurred,
- *         see #MHD_FEATURE_EXTERN_HASH)
- * @sa #MHD_digest_auth_calc_userhash_hex()
- * @note Available since #MHD_VERSION 0x00097701
- * @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;
-  enum MHD_Result ret;
-
-  if (! digest_init_one_time (&da, get_base_digest_algo (algo3)))
-    return MHD_NO;
-  if (digest_get_size (&da) > bin_buf_size)
-    ret = MHD_NO;
-  else
-  {
-    calc_userhash (&da,
-                   username,
-                   strlen (username),
-                   realm,
-                   strlen (realm),
-                   userhash_bin);
-    ret = MHD_YES;
-
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-    if (digest_ext_error (&da))
-      ret = MHD_NO;
-#endif /* MHD_DIGEST_HAS_EXT_ERROR */
-  }
-  digest_deinit (&da);
-
-  return ret;
-}
-
-
-/**
- * Calculate "userhash", return it as hexadecimal string.
- *
- * 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 by 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 string;
- *                          if this function succeeds, then this buffer has
- *                          #MHD_digest_get_hash_size(algo3)*2 chars long
- *                          userhash zero-terminated 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 (or external error has occurred,
- *         see #MHD_FEATURE_EXTERN_HASH).
- * @sa #MHD_digest_auth_calc_userhash()
- * @note Available since #MHD_VERSION 0x00097701
- * @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;
-  size_t num_headers;
-};
-
-/**
- * Test if the given key-value pair is in the headers for the
- * given connection.
- *
- * @param cls the test context
- * @param key the key
- * @param key_size number of bytes in @a key
- * @param value the value, can be NULL
- * @param value_size number of bytes in @a value
- * @param kind type of the header
- * @return #MHD_YES if the key-value pair is in the headers,
- *         #MHD_NO if not
- */
-static enum MHD_Result
-test_header (void *cls,
-             const char *key,
-             size_t key_size,
-             const char *value,
-             size_t value_size,
-             enum MHD_ValueKind kind)
-{
-  struct test_header_param *const param = (struct test_header_param *) cls;
-  struct MHD_Connection *connection = param->connection;
-  struct MHD_HTTP_Req_Header *pos;
-  size_t i;
-
-  param->num_headers++;
-  i = 0;
-  for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
-  {
-    if (kind != pos->kind)
-      continue;
-    if (++i == param->num_headers)
-    {
-      if (key_size != pos->header_size)
-        return MHD_NO;
-      if (value_size != pos->value_size)
-        return MHD_NO;
-      if (0 != key_size)
-      {
-        mhd_assert (NULL != key);
-        mhd_assert (NULL != pos->header);
-        if (0 != memcmp (key,
-                         pos->header,
-                         key_size))
-          return MHD_NO;
-      }
-      if (0 != value_size)
-      {
-        mhd_assert (NULL != value);
-        mhd_assert (NULL != pos->value);
-        if (0 != memcmp (value,
-                         pos->value,
-                         value_size))
-          return MHD_NO;
-      }
-      return MHD_YES;
-    }
-  }
-  return MHD_NO;
-}
-
-
-/**
- * Check that the arguments given by the client as part
- * of the authentication header match the arguments we
- * got as part of the HTTP request URI.
- *
- * @param connection connections with headers to compare against
- * @param args the copy of argument URI string (after "?" in URI), will be
- *             modified by this function
- * @return boolean true if the arguments match,
- *         boolean false if not
- */
-static bool
-check_argument_match (struct MHD_Connection *connection,
-                      char *args)
-{
-  struct MHD_HTTP_Req_Header *pos;
-  enum MHD_Result ret;
-  struct test_header_param param;
-
-  param.connection = connection;
-  param.num_headers = 0;
-  ret = MHD_parse_arguments_ (connection,
-                              MHD_GET_ARGUMENT_KIND,
-                              args,
-                              &test_header,
-                              &param);
-  if (MHD_NO == ret)
-  {
-    return false;
-  }
-  /* also check that the number of headers matches */
-  for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
-  {
-    if (MHD_GET_ARGUMENT_KIND != pos->kind)
-      continue;
-    param.num_headers--;
-  }
-  if (0 != param.num_headers)
-  {
-    /* argument count mismatch */
-    return false;
-  }
-  return true;
-}
-
-
-/**
- * Check that the URI provided by the client as part
- * of the authentication header match the real HTTP request URI.
- *
- * @param connection connections with headers to compare against
- * @param uri the copy of URI in the authentication header, should point to
- *            modifiable buffer at least @a uri_len + 1 characters long,
- *            will be modified by this function, not valid upon return
- * @param uri_len the length of the @a uri string in characters
- * @return boolean true if the URIs match,
- *         boolean false if not
- */
-static bool
-check_uri_match (struct MHD_Connection *connection, char *uri, size_t uri_len)
-{
-  char *qmark;
-  char *args;
-  struct MHD_Daemon *const daemon = connection->daemon;
-
-  uri[uri_len] = 0;
-  qmark = memchr (uri,
-                  '?',
-                  uri_len);
-  if (NULL != qmark)
-    *qmark = '\0';
-
-  /* Need to unescape URI before comparing with connection->url */
-  uri_len = daemon->unescape_callback (daemon->unescape_callback_cls,
-                                       connection,
-                                       uri);
-  if ((uri_len != connection->rq.url_len) ||
-      (0 != memcmp (uri, connection->rq.url, uri_len)))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (daemon,
-              _ ("Authentication failed, URI does not match.\n"));
-#endif
-    return false;
-  }
-
-  args = (NULL != qmark) ? (qmark + 1) : uri + uri_len;
-
-  if (! check_argument_match (connection,
-                              args) )
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (daemon,
-              _ ("Authentication failed, arguments do not match.\n"));
-#endif
-    return false;
-  }
-  return true;
-}
-
-
-/**
- * The size of the unquoting buffer in stack
- */
-#define _MHD_STATIC_UNQ_BUFFER_SIZE 128
-
-
-/**
- * Get the pointer to buffer with required size
- * @param tmp1 the first buffer with fixed size
- * @param ptmp2 the pointer to pointer to malloc'ed buffer
- * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
- * @param required_size the required size in buffer
- * @return the pointer to the buffer or NULL if failed to allocate buffer with
- *         requested size
- */
-static char *
-get_buffer_for_size (char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE],
-                     char **ptmp2,
-                     size_t *ptmp2_size,
-                     size_t required_size)
-{
-  mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2));
-  mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size));
-  mhd_assert ((0 == *ptmp2_size) || \
-              (_MHD_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size));
-
-  if (required_size <= _MHD_STATIC_UNQ_BUFFER_SIZE)
-    return tmp1;
-
-  if (required_size <= *ptmp2_size)
-    return *ptmp2;
-
-  if (required_size > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE)
-    return NULL;
-  if (NULL != *ptmp2)
-    free (*ptmp2);
-  *ptmp2 = (char *) malloc (required_size);
-  if (NULL == *ptmp2)
-    *ptmp2_size = 0;
-  else
-    *ptmp2_size = required_size;
-  return *ptmp2;
-}
-
-
-/**
-  * The result of parameter unquoting
-  */
-enum _MHD_GetUnqResult
-{
-  _MHD_UNQ_OK = 0,         /**< Got unquoted string */
-  _MHD_UNQ_TOO_LARGE = -7, /**< The string is too large to unquote */
-  _MHD_UNQ_OUT_OF_MEM = 3  /**< Out of memory error */
-};
-
-/**
- * Get Digest authorisation parameter as unquoted string.
- * @param param the parameter to process
- * @param tmp1 the small buffer in stack
- * @param ptmp2 the pointer to pointer to malloc'ed buffer
- * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
- * @param[out] unquoted the pointer to store the result, NOT zero terminated
- * @return enum code indicating result of the process
- */
-static enum _MHD_GetUnqResult
-get_unquoted_param (const struct MHD_RqDAuthParam *param,
-                    char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE],
-                    char **ptmp2,
-                    size_t *ptmp2_size,
-                    struct _MHD_str_w_len *unquoted)
-{
-  char *str;
-  size_t len;
-  mhd_assert (NULL != param->value.str);
-  mhd_assert (0 != param->value.len);
-
-  if (! param->quoted)
-  {
-    unquoted->str = param->value.str;
-    unquoted->len = param->value.len;
-    return _MHD_UNQ_OK;
-  }
-  /* The value is present and is quoted, needs to be copied and unquoted */
-  str = get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len);
-  if (NULL == str)
-    return (param->value.len > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ?
-           _MHD_UNQ_TOO_LARGE : _MHD_UNQ_OUT_OF_MEM;
-
-  len = MHD_str_unquote (param->value.str, param->value.len, str);
-  unquoted->str = str;
-  unquoted->len = len;
-  mhd_assert (0 != unquoted->len);
-  mhd_assert (unquoted->len < param->value.len);
-  return _MHD_UNQ_OK;
-}
-
-
-/**
- * Get copy of Digest authorisation parameter as unquoted string.
- * @param param the parameter to process
- * @param tmp1 the small buffer in stack
- * @param ptmp2 the pointer to pointer to malloc'ed buffer
- * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
- * @param[out] unquoted the pointer to store the result, NOT zero terminated,
- *                      but with enough space to zero-terminate
- * @return enum code indicating result of the process
- */
-static enum _MHD_GetUnqResult
-get_unquoted_param_copy (const struct MHD_RqDAuthParam *param,
-                         char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE],
-                         char **ptmp2,
-                         size_t *ptmp2_size,
-                         struct _MHD_mstr_w_len *unquoted)
-{
-  mhd_assert (NULL != param->value.str);
-  mhd_assert (0 != param->value.len);
-
-  /* The value is present and is quoted, needs to be copied and unquoted */
-  /* Allocate buffer with one more additional byte for zero-termination */
-  unquoted->str =
-    get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len + 1);
-
-  if (NULL == unquoted->str)
-    return (param->value.len + 1 > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ?
-           _MHD_UNQ_TOO_LARGE : _MHD_UNQ_OUT_OF_MEM;
-
-  if (! param->quoted)
-  {
-    memcpy (unquoted->str, param->value.str, param->value.len);
-    unquoted->len = param->value.len;
-    return _MHD_UNQ_OK;
-  }
-
-  unquoted->len =
-    MHD_str_unquote (param->value.str, param->value.len, unquoted->str);
-  mhd_assert (0 != unquoted->len);
-  mhd_assert (unquoted->len < param->value.len);
-  return _MHD_UNQ_OK;
-}
-
-
-/**
- * Check whether Digest Auth request parameter is equal to given string
- * @param param the parameter to check
- * @param str the string to compare with, does not need to be zero-terminated
- * @param str_len the length of the @a str
- * @return true is parameter is equal to the given string,
- *         false otherwise
- */
-_MHD_static_inline bool
-is_param_equal (const struct MHD_RqDAuthParam *param,
-                const char *const str,
-                const size_t str_len)
-{
-  mhd_assert (NULL != param->value.str);
-  mhd_assert (0 != param->value.len);
-  if (param->quoted)
-    return MHD_str_equal_quoted_bin_n (param->value.str, param->value.len,
-                                       str, str_len);
-  return (str_len == param->value.len) &&
-         (0 == memcmp (str, param->value.str, str_len));
-
-}
-
-
-/**
- * Check whether Digest Auth request parameter is caseless equal to given string
- * @param param the parameter to check
- * @param str the string to compare with, does not need to be zero-terminated
- * @param str_len the length of the @a str
- * @return true is parameter is caseless equal to the given string,
- *         false otherwise
- */
-_MHD_static_inline bool
-is_param_equal_caseless (const struct MHD_RqDAuthParam *param,
-                         const char *const str,
-                         const size_t str_len)
-{
-  mhd_assert (NULL != param->value.str);
-  mhd_assert (0 != param->value.len);
-  if (param->quoted)
-    return MHD_str_equal_quoted_bin_n (param->value.str, param->value.len,
-                                       str, str_len);
-  return (str_len == param->value.len) &&
-         (0 == memcmp (str, param->value.str, str_len));
-
-}
-
-
-/**
- * Authenticates the authorization header sent by the client
- *
- * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
- * @a mqop and the client uses this mode, then server generated nonces are
- * used as one-time nonces because nonce-count is not supported in this old RFC.
- * Communication in this mode is very inefficient, especially if the client
- * requests several resources one-by-one as for every request new nonce must be
- * generated and client repeat all requests twice (the first time to get a new
- * nonce and the second time to perform an authorised request).
- *
- * @param connection the MHD connection structure
- * @param realm the realm for authorization of the client
- * @param username the username to be authenticated, must be in clear text
- *                 even if userhash is used by the client
- * @param password the password used in the authentication,
- *                 must be NULL if @a userdigest is not NULL
- * @param userdigest the precalculated binary hash of the string
- *                   "username:realm:password",
- *                   must be NULL if @a password is not NULL
- * @param nonce_timeout the period of seconds since nonce generation, when
- *                      the nonce is recognised as valid and not stale;
- *                      unlike #digest_auth_check_all() zero is used literally
- * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
- *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
- *               returned;
- *               unlike #digest_auth_check_all() zero is treated as "no limit"
- * @param mqop the QOP to use
- * @param malgo3 digest algorithms allowed to use, fail if algorithm specified
- *               by the client is not allowed by this parameter
- * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer,
- *                  to be freed if not NULL upon return
- * @return #MHD_DAUTH_OK if authenticated,
- *         error code otherwise.
- * @ingroup authentication
- */
-static enum MHD_DigestAuthResult
-digest_auth_check_all_inner (struct MHD_Connection *connection,
-                             const char *realm,
-                             const char *username,
-                             const char *password,
-                             const uint8_t *userdigest,
-                             unsigned int nonce_timeout,
-                             uint32_t max_nc,
-                             enum MHD_DigestAuthMultiQOP mqop,
-                             enum MHD_DigestAuthMultiAlgo3 malgo3,
-                             char **pbuf,
-                             struct DigestAlgorithm *da)
-{
-  struct MHD_Daemon *daemon = MHD_get_master (connection->daemon);
-  enum MHD_DigestAuthAlgo3 c_algo; /**< Client's algorithm */
-  enum MHD_DigestAuthQOP c_qop; /**< Client's QOP */
-  unsigned int digest_size;
-  uint8_t hash1_bin[MAX_DIGEST];
-  uint8_t hash2_bin[MAX_DIGEST];
-#if 0
-  const char *hentity = NULL; /* "auth-int" is not supported */
-#endif
-  uint64_t nonce_time;
-  uint64_t nci;
-  const struct MHD_RqDAuth *params;
-  /**
-   * Temporal buffer in stack for unquoting and other needs
-   */
-  char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE];
-  char **const ptmp2 = pbuf;     /**< Temporal malloc'ed buffer for unquoting */
-  size_t tmp2_size; /**< The size of @a tmp2 buffer */
-  struct _MHD_str_w_len unquoted;
-  struct _MHD_mstr_w_len unq_copy;
-  enum _MHD_GetUnqResult unq_res;
-  size_t username_len;
-  size_t realm_len;
-
-  mhd_assert ((NULL != password) || (NULL != userdigest));
-  mhd_assert (! ((NULL != userdigest) && (NULL != password)));
-
-  tmp2_size = 0;
-
-  params = MHD_get_rq_dauth_params_ (connection);
-  if (NULL == params)
-    return MHD_DAUTH_WRONG_HEADER;
-
-  /* ** Initial parameters checks and setup ** */
-  /* Get client's algorithm */
-  c_algo = params->algo3;
-  /* Check whether client's algorithm is allowed by function parameter */
-  if (((unsigned int) c_algo) !=
-      (((unsigned int) c_algo) & ((unsigned int) malgo3)))
-    return MHD_DAUTH_WRONG_ALGO;
-  /* Check whether client's algorithm is supported */
-  if (0 != (((unsigned int) c_algo) & MHD_DIGEST_AUTH_ALGO3_SESSION))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The 'session' algorithms are not supported.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_DAUTH_WRONG_ALGO;
-  }
-#ifndef MHD_MD5_SUPPORT
-  if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The MD5 algorithm is not supported by this MHD build.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_DAUTH_WRONG_ALGO;
-  }
-#endif /* ! MHD_MD5_SUPPORT */
-#ifndef MHD_SHA256_SUPPORT
-  if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA256))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The SHA-256 algorithm is not supported by "
-                 "this MHD build.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_DAUTH_WRONG_ALGO;
-  }
-#endif /* ! MHD_SHA256_SUPPORT */
-#ifndef MHD_SHA512_256_SUPPORT
-  if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA512_256))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The SHA-512/256 algorithm is not supported by "
-                 "this MHD build.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_DAUTH_WRONG_ALGO;
-  }
-#endif /* ! MHD_SHA512_256_SUPPORT */
-  if (! digest_init_one_time (da, get_base_digest_algo (c_algo)))
-    MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
-  /* Check 'mqop' value */
-  c_qop = params->qop;
-  /* Check whether client's QOP is allowed by function parameter */
-  if (((unsigned int) c_qop) !=
-      (((unsigned int) c_qop) & ((unsigned int) mqop)))
-    return MHD_DAUTH_WRONG_QOP;
-  if (0 != (((unsigned int) c_qop) & MHD_DIGEST_AUTH_QOP_AUTH_INT))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The 'auth-int' QOP is not supported.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_DAUTH_WRONG_QOP;
-  }
-#ifdef HAVE_MESSAGES
-  if ((MHD_DIGEST_AUTH_QOP_NONE == c_qop) &&
-      (0 == (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5)))
-    MHD_DLOG (connection->daemon,
-              _ ("RFC2069 with SHA-256 or SHA-512/256 algorithm is " \
-                 "non-standard extension.\n"));
-#endif /* HAVE_MESSAGES */
-
-  digest_size = digest_get_size (da);
-
-  /* ** A quick check for presence of all required parameters ** */
-
-  if ((NULL == params->username.value.str) &&
-      (NULL == params->username_ext.value.str))
-    return MHD_DAUTH_WRONG_USERNAME;
-  else if ((NULL != params->username.value.str) &&
-           (NULL != params->username_ext.value.str))
-    return MHD_DAUTH_WRONG_USERNAME; /* Parameters cannot be used together */
-  else if ((NULL != params->username_ext.value.str) &&
-           (MHD_DAUTH_EXT_PARAM_MIN_LEN > params->username_ext.value.len))
-    return MHD_DAUTH_WRONG_USERNAME;  /* Broken extended notation */
-  else if (params->userhash && (NULL == params->username.value.str))
-    return MHD_DAUTH_WRONG_USERNAME;  /* Userhash cannot be used with extended notation */
-  else if (params->userhash && (digest_size * 2 > params->username.value.len))
-    return MHD_DAUTH_WRONG_USERNAME;  /* Too few chars for correct userhash */
-  else if (params->userhash && (digest_size * 4 < params->username.value.len))
-    return MHD_DAUTH_WRONG_USERNAME;  /* Too many chars for correct userhash */
-
-  if (NULL == params->realm.value.str)
-    return MHD_DAUTH_WRONG_REALM;
-  else if (((NULL == userdigest) || params->userhash) &&
-           (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len))
-    return MHD_DAUTH_TOO_LARGE; /* Realm is too large and should be used in hash calculations */
-
-  if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
-  {
-    if (NULL == params->nc.value.str)
-      return MHD_DAUTH_WRONG_HEADER;
-    else if (0 == params->nc.value.len)
-      return MHD_DAUTH_WRONG_HEADER;
-    else if (4 * 8 < params->nc.value.len) /* Four times more than needed */
-      return MHD_DAUTH_WRONG_HEADER;
-
-    if (NULL == params->cnonce.value.str)
-      return MHD_DAUTH_WRONG_HEADER;
-    else if (0 == params->cnonce.value.len)
-      return MHD_DAUTH_WRONG_HEADER;
-    else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len)
-      return MHD_DAUTH_TOO_LARGE;
-  }
-
-  /* The QOP parameter was checked already */
-
-  if (NULL == params->uri.value.str)
-    return MHD_DAUTH_WRONG_URI;
-  else if (0 == params->uri.value.len)
-    return MHD_DAUTH_WRONG_URI;
-  else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->uri.value.len)
-    return MHD_DAUTH_TOO_LARGE;
-
-  if (NULL == params->nonce.value.str)
-    return MHD_DAUTH_NONCE_WRONG;
-  else if (0 == params->nonce.value.len)
-    return MHD_DAUTH_NONCE_WRONG;
-  else if (NONCE_STD_LEN (digest_size) * 2 < params->nonce.value.len)
-    return MHD_DAUTH_NONCE_WRONG;
-
-  if (NULL == params->response.value.str)
-    return MHD_DAUTH_RESPONSE_WRONG;
-  else if (0 == params->response.value.len)
-    return MHD_DAUTH_RESPONSE_WRONG;
-  else if (digest_size * 4 < params->response.value.len)
-    return MHD_DAUTH_RESPONSE_WRONG;
-
-  /* ** Check simple parameters match ** */
-
-  /* Check 'algorithm' */
-  /* The 'algorithm' was checked at the start of the function */
-  /* 'algorithm' valid */
-
-  /* Check 'qop' */
-  /* The 'qop' was checked at the start of the function */
-  /* 'qop' valid */
-
-  /* Check 'realm' */
-  realm_len = strlen (realm);
-  if (! is_param_equal (&params->realm, realm, realm_len))
-    return MHD_DAUTH_WRONG_REALM;
-  /* 'realm' valid */
-
-  /* Check 'username' */
-  username_len = strlen (username);
-  if (! params->userhash)
-  {
-    if (NULL != params->username.value.str)
-    { /* Username in standard notation */
-      if (! is_param_equal (&params->username, username, username_len))
-        return MHD_DAUTH_WRONG_USERNAME;
-    }
-    else
-    { /* Username in extended notation */
-      char *r_uname;
-      size_t buf_size = params->username_ext.value.len;
-      ssize_t res;
-
-      mhd_assert (NULL != params->username_ext.value.str);
-      mhd_assert (MHD_DAUTH_EXT_PARAM_MIN_LEN <= buf_size); /* It was checked already */
-      buf_size += 1; /* For zero-termination */
-      buf_size -= MHD_DAUTH_EXT_PARAM_MIN_LEN;
-      r_uname = get_buffer_for_size (tmp1, ptmp2, &tmp2_size, buf_size);
-      if (NULL == r_uname)
-        return (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < buf_size) ?
-               MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR;
-      res = get_rq_extended_uname_copy_z (params->username_ext.value.str,
-                                          params->username_ext.value.len,
-                                          r_uname, buf_size);
-      if (0 > res)
-        return MHD_DAUTH_WRONG_HEADER; /* Broken extended notation */
-      if ((username_len != (size_t) res) ||
-          (0 != memcmp (username, r_uname, username_len)))
-        return MHD_DAUTH_WRONG_USERNAME;
-    }
-  }
-  else
-  { /* Userhash */
-    mhd_assert (NULL != params->username.value.str);
-    calc_userhash (da, username, username_len, realm, realm_len, hash1_bin);
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-    if (digest_ext_error (da))
-      return MHD_DAUTH_ERROR;
-#endif /* MHD_DIGEST_HAS_EXT_ERROR */
-    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))
-      return MHD_DAUTH_WRONG_USERNAME;
-    /* To simplify the logic, the digest is reset here instead of resetting
-       before the next hash calculation. */
-    digest_reset (da);
-  }
-  /* 'username' valid */
-
-  /* ** Do basic nonce and nonce-counter checks (size, timestamp) ** */
-
-  /* Get 'nc' digital value */
-  if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
-  {
-
-    unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
-                                  &unquoted);
-    if (_MHD_UNQ_OK != unq_res)
-      return MHD_DAUTH_ERROR;
-
-    if (unquoted.len != MHD_strx_to_uint64_n_ (unquoted.str,
-                                               unquoted.len,
-                                               &nci))
-    {
-#ifdef HAVE_MESSAGES
-      MHD_DLOG (daemon,
-                _ ("Authentication failed, invalid nc format.\n"));
-#endif
-      return MHD_DAUTH_WRONG_HEADER;   /* invalid nonce format */
-    }
-    if (0 == nci)
-    {
-#ifdef HAVE_MESSAGES
-      MHD_DLOG (daemon,
-                _ ("Authentication failed, invalid 'nc' value.\n"));
-#endif
-      return MHD_DAUTH_WRONG_HEADER;   /* invalid nc value */
-    }
-    if ((0 != max_nc) && (max_nc < nci))
-      return MHD_DAUTH_NONCE_STALE;    /* Too large 'nc' value */
-  }
-  else
-    nci = 1; /* Force 'nc' value */
-  /* Got 'nc' digital value */
-
-  /* Get 'nonce' with basic checks */
-  unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
-                                &unquoted);
-  if (_MHD_UNQ_OK != unq_res)
-    return MHD_DAUTH_ERROR;
-
-  if ((NONCE_STD_LEN (digest_size) != unquoted.len) ||
-      (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time)))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (daemon,
-              _ ("Authentication failed, invalid nonce format.\n"));
-#endif
-    return MHD_DAUTH_NONCE_WRONG;
-  }
-
-  if (1)
-  {
-    uint64_t t;
-
-    t = MHD_monotonic_msec_counter ();
-    /*
-     * First level vetting for the nonce validity: if the timestamp
-     * attached to the nonce exceeds `nonce_timeout', then the nonce is
-     * stale.
-     */
-    if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000))
-      return MHD_DAUTH_NONCE_STALE; /* too old */
-  }
-  if (1)
-  {
-    enum MHD_CheckNonceNC_ nonce_nc_check;
-    /*
-     * Checking if that combination of nonce and nc is sound
-     * and not a replay attack attempt. Refuse if nonce was not
-     * generated previously.
-     */
-    nonce_nc_check = check_nonce_nc (connection,
-                                     unquoted.str,
-                                     NONCE_STD_LEN (digest_size),
-                                     nonce_time,
-                                     nci);
-    if (MHD_CHECK_NONCENC_STALE == nonce_nc_check)
-    {
-#ifdef HAVE_MESSAGES
-      if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
-        MHD_DLOG (daemon,
-                  _ ("Stale nonce received. If this happens a lot, you should "
-                     "probably increase the size of the nonce array.\n"));
-      else
-        MHD_DLOG (daemon,
-                  _ ("Stale nonce received. This is expected when client " \
-                     "uses RFC2069-compatible mode and makes more than one " \
-                     "request.\n"));
-#endif
-      return MHD_DAUTH_NONCE_STALE;
-    }
-    else if (MHD_CHECK_NONCENC_WRONG == nonce_nc_check)
-    {
-#ifdef HAVE_MESSAGES
-      MHD_DLOG (daemon,
-                _ ("Received nonce that was not "
-                   "generated by MHD. This may indicate an attack attempt.\n"));
-#endif
-      return MHD_DAUTH_NONCE_WRONG;
-    }
-    mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check);
-  }
-  /* The nonce was generated by MHD, is not stale and nonce-nc combination was
-     not used before */
-
-  /* ** Build H(A2) and check URI match in the header and in the request ** */
-
-  /* Get 'uri' */
-  mhd_assert (! da->hashing);
-  digest_update_str (da, connection->rq.method);
-  digest_update_with_colon (da);
-#if 0
-  /* TODO: add support for "auth-int" */
-  digest_update_str (da, hentity);
-  digest_update_with_colon (da);
-#endif
-  unq_res = get_unquoted_param_copy (&params->uri, tmp1, ptmp2, &tmp2_size,
-                                     &unq_copy);
-  if (_MHD_UNQ_OK != unq_res)
-    return MHD_DAUTH_ERROR;
-
-  digest_update (da, unq_copy.str, unq_copy.len);
-  /* The next check will modify copied URI string */
-  if (! check_uri_match (connection, unq_copy.str, unq_copy.len))
-    return MHD_DAUTH_WRONG_URI;
-  digest_calc_hash (da, hash2_bin);
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-  /* Skip digest calculation external error check, the next one checks both */
-#endif /* MHD_DIGEST_HAS_EXT_ERROR */
-  /* Got H(A2) */
-
-  /* ** Build H(A1) ** */
-  if (NULL == userdigest)
-  {
-    mhd_assert (! da->hashing);
-    digest_reset (da);
-    calc_userdigest (da,
-                     username, username_len,
-                     realm, realm_len,
-                     password,
-                     hash1_bin);
-  }
-  /* TODO: support '-sess' versions */
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-  if (digest_ext_error (da))
-    return MHD_DAUTH_ERROR;
-#endif /* MHD_DIGEST_HAS_EXT_ERROR */
-  /* Got H(A1) */
-
-  /* **  Check 'response' ** */
-
-  mhd_assert (! da->hashing);
-  digest_reset (da);
-  /* Update digest with H(A1) */
-  mhd_assert (sizeof (tmp1) >= (digest_size * 2));
-  if (NULL == userdigest)
-    MHD_bin_to_hex (hash1_bin, digest_size, tmp1);
-  else
-    MHD_bin_to_hex (userdigest, digest_size, tmp1);
-  digest_update (da, (const uint8_t *) tmp1, digest_size * 2);
-
-  /* H(A1) is not needed anymore, reuse the buffer.
-   * Use hash1_bin for the client's 'response' decoded to binary form. */
-  unq_res = get_unquoted_param (&params->response, tmp1, ptmp2, &tmp2_size,
-                                &unquoted);
-  if (_MHD_UNQ_OK != unq_res)
-    return MHD_DAUTH_ERROR;
-  if (digest_size != MHD_hex_to_bin (unquoted.str, unquoted.len, hash1_bin))
-    return MHD_DAUTH_RESPONSE_WRONG;
-
-  /* Update digest with ':' */
-  digest_update_with_colon (da);
-  /* Update digest with 'nonce' text value */
-  unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
-                                &unquoted);
-  if (_MHD_UNQ_OK != unq_res)
-    return MHD_DAUTH_ERROR;
-  digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
-  /* Update digest with ':' */
-  digest_update_with_colon (da);
-  if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
-  {
-    /* Update digest with 'nc' text value */
-    unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
-                                  &unquoted);
-    if (_MHD_UNQ_OK != unq_res)
-      return MHD_DAUTH_ERROR;
-    digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
-    /* Update digest with ':' */
-    digest_update_with_colon (da);
-    /* Update digest with 'cnonce' value */
-    unq_res = get_unquoted_param (&params->cnonce, tmp1, ptmp2, &tmp2_size,
-                                  &unquoted);
-    if (_MHD_UNQ_OK != unq_res)
-      return MHD_DAUTH_ERROR;
-    digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
-    /* Update digest with ':' */
-    digest_update_with_colon (da);
-    /* Update digest with 'qop' value */
-    unq_res = get_unquoted_param (&params->qop_raw, tmp1, ptmp2, &tmp2_size,
-                                  &unquoted);
-    if (_MHD_UNQ_OK != unq_res)
-      return MHD_DAUTH_ERROR;
-    digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
-    /* Update digest with ':' */
-    digest_update_with_colon (da);
-  }
-  /* Update digest with H(A2) */
-  MHD_bin_to_hex (hash2_bin, digest_size, tmp1);
-  digest_update (da, (const uint8_t *) tmp1, digest_size * 2);
-
-  /* H(A2) is not needed anymore, reuse the buffer.
-   * Use hash2_bin for the calculated response in binary form */
-  digest_calc_hash (da, hash2_bin);
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-  if (digest_ext_error (da))
-    return MHD_DAUTH_ERROR;
-#endif /* MHD_DIGEST_HAS_EXT_ERROR */
-
-  if (0 != memcmp (hash1_bin, hash2_bin, digest_size))
-    return MHD_DAUTH_RESPONSE_WRONG;
-
-  if (MHD_DAUTH_BIND_NONCE_NONE != daemon->dauth_bind_type)
-  {
-    mhd_assert (sizeof(tmp1) >= (NONCE_STD_LEN (digest_size) + 1));
-    /* It was already checked that 'nonce' (including timestamp) was generated
-       by MHD. */
-    mhd_assert (! da->hashing);
-    digest_reset (da);
-    calculate_nonce (nonce_time,
-                     connection->rq.http_mthd,
-                     connection->rq.method,
-                     daemon->digest_auth_random,
-                     daemon->digest_auth_rand_size,
-                     connection->addr,
-                     (size_t) connection->addr_len,
-                     connection->rq.url,
-                     connection->rq.url_len,
-                     connection->rq.headers_received,
-                     realm,
-                     realm_len,
-                     daemon->dauth_bind_type,
-                     da,
-                     tmp1);
-
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-    if (digest_ext_error (da))
-      return MHD_DAUTH_ERROR;
-#endif /* MHD_DIGEST_HAS_EXT_ERROR */
-
-    if (! is_param_equal (&params->nonce, tmp1,
-                          NONCE_STD_LEN (digest_size)))
-      return MHD_DAUTH_NONCE_OTHER_COND;
-    /* The 'nonce' was generated in the same conditions */
-  }
-
-  return MHD_DAUTH_OK;
-}
-
-
-/**
- * Authenticates the authorization header sent by the client
- *
- * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
- * @a mqop and the client uses this mode, then server generated nonces are
- * used as one-time nonces because nonce-count is not supported in this old RFC.
- * Communication in this mode is very inefficient, especially if the client
- * requests several resources one-by-one as for every request new nonce must be
- * generated and client repeat all requests twice (the first time to get a new
- * nonce and the second time to perform an authorised request).
- *
- * @param connection the MHD connection structure
- * @param realm the realm for authorization of the client
- * @param username the username to be authenticated, must be in clear text
- *                 even if userhash is used by the client
- * @param password the password used in the authentication,
- *                 must be NULL if @a userdigest is not NULL
- * @param userdigest the precalculated binary hash of the string
- *                   "username:realm:password",
- *                   must be NULL if @a password is not NULL
- * @param nonce_timeout the period of seconds since nonce generation, when
- *                      the nonce is recognised as valid and not stale;
- *                      if set to zero then daemon's default value is used
- * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
- *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
- *               returned;
- *               if set to zero then daemon's default value is used
- * @param mqop the QOP to use
- * @param malgo3 digest algorithms allowed to use, fail if algorithm specified
- *               by the client is not allowed by this parameter
- * @return #MHD_DAUTH_OK if authenticated,
- *         error code otherwise.
- * @ingroup authentication
- */
-static enum MHD_DigestAuthResult
-digest_auth_check_all (struct MHD_Connection *connection,
-                       const char *realm,
-                       const char *username,
-                       const char *password,
-                       const uint8_t *userdigest,
-                       unsigned int nonce_timeout,
-                       uint32_t max_nc,
-                       enum MHD_DigestAuthMultiQOP mqop,
-                       enum MHD_DigestAuthMultiAlgo3 malgo3)
-{
-  enum MHD_DigestAuthResult res;
-  char *buf;
-  struct DigestAlgorithm da;
-
-  buf = NULL;
-  digest_setup_zero (&da);
-  if (0 == nonce_timeout)
-    nonce_timeout = connection->daemon->dauth_def_nonce_timeout;
-  if (0 == max_nc)
-    max_nc = connection->daemon->dauth_def_max_nc;
-  res = digest_auth_check_all_inner (connection, realm, username, password,
-                                     userdigest,
-                                     nonce_timeout,
-                                     max_nc, mqop, malgo3,
-                                     &buf, &da);
-  digest_deinit (&da);
-  if (NULL != buf)
-    free (buf);
-
-  return res;
-}
-
-
-/**
- * Authenticates the authorization header sent by the client.
- * Uses #MHD_DIGEST_ALG_MD5 (for now, for backwards-compatibility).
- * Note that this MAY change to #MHD_DIGEST_ALG_AUTO in the future.
- * If you want to be sure you get MD5, use #MHD_digest_auth_check2()
- * and specify MD5 explicitly.
- *
- * @param connection The MHD connection structure
- * @param realm The realm presented to the client
- * @param username The username needs to be authenticated
- * @param password The password used in the authentication
- * @param nonce_timeout The amount of time for a nonce to be
- *      invalid in seconds
- * @return #MHD_YES if authenticated, #MHD_NO if not,
- *         #MHD_INVALID_NONCE if nonce is invalid or stale
- * @deprecated use MHD_digest_auth_check3()
- * @ingroup authentication
- */
-_MHD_EXTERN int
-MHD_digest_auth_check (struct MHD_Connection *connection,
-                       const char *realm,
-                       const char *username,
-                       const char *password,
-                       unsigned int nonce_timeout)
-{
-  return MHD_digest_auth_check2 (connection,
-                                 realm,
-                                 username,
-                                 password,
-                                 nonce_timeout,
-                                 MHD_DIGEST_ALG_MD5);
-}
-
-
-/**
- * Authenticates the authorization header sent by the client.
- *
- * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
- * @a mqop and the client uses this mode, then server generated nonces are
- * used as one-time nonces because nonce-count is not supported in this old RFC.
- * Communication in this mode is very inefficient, especially if the client
- * requests several resources one-by-one as for every request a new nonce must
- * be generated and client repeats all requests twice (first time to get a new
- * nonce and second time to perform an authorised request).
- *
- * @param connection the MHD connection structure
- * @param realm the realm for authorization of the client
- * @param username the username to be authenticated, must be in clear text
- *                 even if userhash is used by the client
- * @param password the password matching the @a username (and the @a realm)
- * @param nonce_timeout the period of seconds since nonce generation, when
- *                      the nonce is recognised as valid and not stale;
- *                      if zero is specified then daemon default value is used.
- * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
- *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
- *               returned;
- *               if zero is specified then daemon default value is used.
- * @param mqop the QOP to use
- * @param malgo3 digest algorithms allowed to use, fail if algorithm used
- *               by the client is not allowed by this parameter
- * @return #MHD_DAUTH_OK if authenticated,
- *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097708
- * @ingroup authentication
- */
-_MHD_EXTERN enum MHD_DigestAuthResult
-MHD_digest_auth_check3 (struct MHD_Connection *connection,
-                        const char *realm,
-                        const char *username,
-                        const char *password,
-                        unsigned int nonce_timeout,
-                        uint32_t max_nc,
-                        enum MHD_DigestAuthMultiQOP mqop,
-                        enum MHD_DigestAuthMultiAlgo3 malgo3)
-{
-  mhd_assert (NULL != password);
-
-  return digest_auth_check_all (connection,
-                                realm,
-                                username,
-                                password,
-                                NULL,
-                                nonce_timeout,
-                                max_nc,
-                                mqop,
-                                malgo3);
-}
-
-
-/**
- * Authenticates the authorization header sent by the client by using
- * hash of "username:realm:password".
- *
- * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in
- * @a mqop and the client uses this mode, then server generated nonces are
- * used as one-time nonces because nonce-count is not supported in this old RFC.
- * Communication in this mode is very inefficient, especially if the client
- * requests several resources one-by-one as for every request a new nonce must
- * be generated and client repeats all requests twice (first time to get a new
- * nonce and second time to perform an authorised request).
- *
- * @param connection the MHD connection structure
- * @param realm the realm for authorization of the client
- * @param username the username 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",
- *                   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_SHA512_256_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;
- *                      if zero is specified then daemon default value is used.
- * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
- *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
- *               returned;
- *               if zero is specified then daemon default value is used.
- * @param mqop the QOP to use
- * @param malgo3 digest algorithms allowed to use, fail if algorithm used
- *               by the client is not allowed by this parameter;
- *               more than one base algorithms (MD5, SHA-256, SHA-512/256)
- *               cannot be used at the same time for this function
- *               as @a userdigest must match specified algorithm
- * @return #MHD_DAUTH_OK if authenticated,
- *         the error code otherwise
- * @sa #MHD_digest_auth_calc_userdigest()
- * @note Available since #MHD_VERSION 0x00097708
- * @ingroup authentication
- */
-_MHD_EXTERN enum MHD_DigestAuthResult
-MHD_digest_auth_check_digest3 (struct MHD_Connection *connection,
-                               const char *realm,
-                               const char *username,
-                               const void *userdigest,
-                               size_t userdigest_size,
-                               unsigned int nonce_timeout,
-                               uint32_t max_nc,
-                               enum MHD_DigestAuthMultiQOP mqop,
-                               enum MHD_DigestAuthMultiAlgo3 malgo3)
-{
-  if (1 != (((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0)
-            + ((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0)
-            + ((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0)))
-    MHD_PANIC (_ ("Wrong 'malgo3' value, only one base hashing algorithm " \
-                  "(MD5, SHA-256 or SHA-512/256) must be specified, " \
-                  "API violation"));
-
-#ifndef MHD_MD5_SUPPORT
-  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The MD5 algorithm is not supported by this MHD build.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_DAUTH_WRONG_ALGO;
-  }
-#endif /* ! MHD_MD5_SUPPORT */
-#ifndef MHD_SHA256_SUPPORT
-  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The SHA-256 algorithm is not supported by "
-                 "this MHD build.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_DAUTH_WRONG_ALGO;
-  }
-#endif /* ! MHD_SHA256_SUPPORT */
-#ifndef MHD_SHA512_256_SUPPORT
-  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA512_256))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The SHA-512/256 algorithm is not supported by "
-                 "this MHD build.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_DAUTH_WRONG_ALGO;
-  }
-#endif /* ! MHD_SHA512_256_SUPPORT */
-
-  if (digest_get_hash_size ((enum MHD_DigestAuthAlgo3) malgo3) !=
-      userdigest_size)
-    MHD_PANIC (_ ("Wrong 'userdigest_size' value, does not match 'malgo3', "
-                  "API violation"));
-
-  return digest_auth_check_all (connection,
-                                realm,
-                                username,
-                                NULL,
-                                (const uint8_t *) userdigest,
-                                nonce_timeout,
-                                max_nc,
-                                mqop,
-                                malgo3);
-}
-
-
-/**
- * Authenticates the authorization header sent by the client.
- *
- * @param connection The MHD connection structure
- * @param realm The realm presented to the client
- * @param username The username needs to be authenticated
- * @param password The password used in the authentication
- * @param nonce_timeout The amount of time for a nonce to be
- *      invalid in seconds
- * @param algo digest algorithms allowed for verification
- * @return #MHD_YES if authenticated, #MHD_NO if not,
- *         #MHD_INVALID_NONCE if nonce is invalid or stale
- * @note Available since #MHD_VERSION 0x00096200
- * @deprecated use MHD_digest_auth_check3()
- * @ingroup authentication
- */
-_MHD_EXTERN int
-MHD_digest_auth_check2 (struct MHD_Connection *connection,
-                        const char *realm,
-                        const char *username,
-                        const char *password,
-                        unsigned int nonce_timeout,
-                        enum MHD_DigestAuthAlgorithm algo)
-{
-  enum MHD_DigestAuthResult res;
-  enum MHD_DigestAuthMultiAlgo3 malgo3;
-
-  if (MHD_DIGEST_ALG_AUTO == algo)
-    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
-  else if (MHD_DIGEST_ALG_MD5 == algo)
-    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5;
-  else if (MHD_DIGEST_ALG_SHA256 == algo)
-    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
-  else
-    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
-
-  res = MHD_digest_auth_check3 (connection,
-                                realm,
-                                username,
-                                password,
-                                nonce_timeout,
-                                0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
-                                malgo3);
-  if (MHD_DAUTH_OK == res)
-    return MHD_YES;
-  else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res) ||
-           (MHD_DAUTH_NONCE_OTHER_COND == res) )
-    return MHD_INVALID_NONCE;
-  return MHD_NO;
-
-}
-
-
-/**
- * Authenticates the authorization header sent by the client.
- *
- * @param connection The MHD connection structure
- * @param realm The realm presented to the client
- * @param username The username needs to be authenticated
- * @param digest An `unsigned char *' pointer to the binary MD5 sum
- *      for the precalculated hash value "username:realm:password"
- *      of @a digest_size bytes
- * @param digest_size number of bytes in @a digest (size must match @a algo!)
- * @param nonce_timeout The amount of time for a nonce to be
- *      invalid in seconds
- * @param algo digest algorithms allowed for verification
- * @return #MHD_YES if authenticated, #MHD_NO if not,
- *         #MHD_INVALID_NONCE if nonce is invalid or stale
- * @note Available since #MHD_VERSION 0x00096200
- * @deprecated use MHD_digest_auth_check_digest3()
- * @ingroup authentication
- */
-_MHD_EXTERN int
-MHD_digest_auth_check_digest2 (struct MHD_Connection *connection,
-                               const char *realm,
-                               const char *username,
-                               const uint8_t *digest,
-                               size_t digest_size,
-                               unsigned int nonce_timeout,
-                               enum MHD_DigestAuthAlgorithm algo)
-{
-  enum MHD_DigestAuthResult res;
-  enum MHD_DigestAuthMultiAlgo3 malgo3;
-
-  if (MHD_DIGEST_ALG_AUTO == algo)
-    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
-  else if (MHD_DIGEST_ALG_MD5 == algo)
-    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5;
-  else if (MHD_DIGEST_ALG_SHA256 == algo)
-    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
-  else
-    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
-
-  res = MHD_digest_auth_check_digest3 (connection,
-                                       realm,
-                                       username,
-                                       digest,
-                                       digest_size,
-                                       nonce_timeout,
-                                       0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
-                                       malgo3);
-  if (MHD_DAUTH_OK == res)
-    return MHD_YES;
-  else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res) ||
-           (MHD_DAUTH_NONCE_OTHER_COND == res) )
-    return MHD_INVALID_NONCE;
-  return MHD_NO;
-}
-
-
-/**
- * Authenticates the authorization header sent by the client
- * Uses #MHD_DIGEST_ALG_MD5 (required, as @a digest is of fixed
- * size).
- *
- * @param connection The MHD connection structure
- * @param realm The realm presented to the client
- * @param username The username needs to be authenticated
- * @param digest An `unsigned char *' pointer to the binary hash
- *    for the precalculated hash value "username:realm:password";
- *    length must be #MHD_MD5_DIGEST_SIZE bytes
- * @param nonce_timeout The amount of time for a nonce to be
- *      invalid in seconds
- * @return #MHD_YES if authenticated, #MHD_NO if not,
- *         #MHD_INVALID_NONCE if nonce is invalid or stale
- * @note Available since #MHD_VERSION 0x00096000
- * @deprecated use #MHD_digest_auth_check_digest3()
- * @ingroup authentication
- */
-_MHD_EXTERN int
-MHD_digest_auth_check_digest (struct MHD_Connection *connection,
-                              const char *realm,
-                              const char *username,
-                              const uint8_t digest[MHD_MD5_DIGEST_SIZE],
-                              unsigned int nonce_timeout)
-{
-  return MHD_digest_auth_check_digest2 (connection,
-                                        realm,
-                                        username,
-                                        digest,
-                                        MHD_MD5_DIGEST_SIZE,
-                                        nonce_timeout,
-                                        MHD_DIGEST_ALG_MD5);
-}
-
-
-/**
- * Internal version of #MHD_queue_auth_required_response3() to simplify
- * cleanups.
- *
- * @param connection the MHD connection structure
- * @param realm the realm presented to the client
- * @param opaque the string for opaque value, can be NULL, but NULL is
- *               not recommended for better compatibility with clients;
- *               the recommended format is hex or Base64 encoded string
- * @param domain the optional space-separated list of URIs for which the
- *               same authorisation could be used, URIs can be in form
- *               "path-absolute" (the path for the same host with initial slash)
- *               or in form "absolute-URI" (the full path with protocol), in
- *               any case client may assume that URI is in the same "protection
- *               space" if it starts with any of values specified here;
- *               could be NULL (clients typically assume that the same
- *               credentials could be used for any URI on the same host)
- * @param response the reply to send; should contain the "access denied"
- *                 body; note that this function sets the "WWW Authenticate"
- *                 header and that the caller should not do this;
- *                 the NULL is tolerated
- * @param signal_stale set to #MHD_YES if the nonce is stale to add 'stale=true'
- *                     to the authentication header, this instructs the client
- *                     to retry immediately with the new nonce and the same
- *                     credentials, without asking user for the new password
- * @param mqop the QOP to use
- * @param malgo3 digest algorithm to use, MHD selects; if several algorithms
- *               are allowed then MD5 is preferred (currently, may be changed
- *               in next versions)
- * @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 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
- * @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
- * @return #MHD_YES on success, #MHD_NO otherwise
- * @note Available since #MHD_VERSION 0x00097701
- * @ingroup authentication
- */
-static enum MHD_Result
-queue_auth_required_response3_inner (struct MHD_Connection *connection,
-                                     const char *realm,
-                                     const char *opaque,
-                                     const char *domain,
-                                     struct MHD_Response *response,
-                                     int signal_stale,
-                                     enum MHD_DigestAuthMultiQOP mqop,
-                                     enum MHD_DigestAuthMultiAlgo3 malgo3,
-                                     int userhash_support,
-                                     int prefer_utf8,
-                                     char **buf_ptr,
-                                     struct DigestAlgorithm *da)
-{
-  static const char prefix_realm[] = "realm=\"";
-  static const char prefix_qop[] = "qop=\"";
-  static const char prefix_algo[] = "algorithm=";
-  static const char prefix_nonce[] = "nonce=\"";
-  static const char prefix_opaque[] = "opaque=\"";
-  static const char prefix_domain[] = "domain=\"";
-  static const char str_charset[] = "charset=UTF-8";
-  static const char str_userhash[] = "userhash=true";
-  static const char str_stale[] = "stale=true";
-  enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */
-  size_t realm_len;
-  size_t opaque_len;
-  size_t domain_len;
-  size_t buf_size;
-  char *buf;
-  size_t p; /* The position in the buffer */
-  char *hdr_name;
-
-  if ((0 == (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_NON_SESSION)) ||
-      (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION)))
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("Only non-'session' algorithms are supported.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_NO;
-  }
-  malgo3 =
-    (enum MHD_DigestAuthMultiAlgo3)
-    (malgo3
-     & (~((enum MHD_DigestAuthMultiAlgo3) MHD_DIGEST_AUTH_ALGO3_NON_SESSION)));
-#ifdef MHD_MD5_SUPPORT
-  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5))
-    s_algo = MHD_DIGEST_AUTH_ALGO3_MD5;
-  else
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256))
-    s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256;
-  else
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA512_256))
-    s_algo = MHD_DIGEST_AUTH_ALGO3_SHA512_256;
-  else
-#endif /* MHD_SHA512_256_SUPPORT */
-  {
-    if (0 == (((unsigned int) malgo3)
-              & (MHD_DIGEST_BASE_ALGO_MD5 | MHD_DIGEST_BASE_ALGO_SHA256
-                 | MHD_DIGEST_BASE_ALGO_SHA512_256)))
-      MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
-    else
-    {
-#ifdef HAVE_MESSAGES
-      MHD_DLOG (connection->daemon,
-                _ ("No requested algorithm is supported by this MHD build.\n"));
-#endif /* HAVE_MESSAGES */
-    }
-    return MHD_NO;
-  }
-
-  if (MHD_DIGEST_AUTH_MULT_QOP_AUTH_INT == mqop)
-    MHD_PANIC (_ ("Wrong 'mqop' value, API violation"));
-
-  mqop = (enum MHD_DigestAuthMultiQOP)
-         (mqop
-          & (~((enum MHD_DigestAuthMultiQOP) MHD_DIGEST_AUTH_QOP_AUTH_INT)));
-
-  if (! digest_init_one_time (da, get_base_digest_algo (s_algo)))
-    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
-
-  if (MHD_DIGEST_AUTH_MULT_QOP_NONE == mqop)
-  {
-#ifdef HAVE_MESSAGES
-    if ((0 != userhash_support) || (0 != prefer_utf8))
-      MHD_DLOG (connection->daemon,
-                _ ("The 'userhash' and 'charset' ('prefer_utf8') parameters " \
-                   "are not compatible with RFC2069 and ignored.\n"));
-    if (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5))
-      MHD_DLOG (connection->daemon,
-                _ ("RFC2069 with SHA-256 or SHA-512/256 algorithm is " \
-                   "non-standard extension.\n"));
-#endif
-    userhash_support = 0;
-    prefer_utf8 = 0;
-  }
-
-  if (0 == MHD_get_master (connection->daemon)->nonce_nc_size)
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The nonce array size is zero.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_NO;
-  }
-
-  /* Calculate required size */
-  buf_size = 0;
-  /* 'Digest ' */
-  buf_size += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE) + 1; /* 1 for ' ' */
-  buf_size += MHD_STATICSTR_LEN_ (prefix_realm) + 3; /* 3 for '", ' */
-  /* 'realm="xxxx", ' */
-  realm_len = strlen (realm);
-  if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < realm_len)
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The 'realm' is too large.\n"));
-#endif /* HAVE_MESSAGES */
-    return MHD_NO;
-  }
-  if ((NULL != memchr (realm, '\r', realm_len)) ||
-      (NULL != memchr (realm, '\n', realm_len)))
-    return MHD_NO;
-
-  buf_size += realm_len * 2; /* Quoting may double the size */
-  /* 'qop="xxxx", ' */
-  if (MHD_DIGEST_AUTH_MULT_QOP_NONE != mqop)
-  {
-    buf_size += MHD_STATICSTR_LEN_ (prefix_qop) + 3; /* 3 for '", ' */
-    buf_size += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_);
-  }
-  /* 'algorithm="xxxx", ' */
-  if (((MHD_DIGEST_AUTH_MULT_QOP_NONE) != mqop) ||
-      (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5)))
-  {
-    buf_size += MHD_STATICSTR_LEN_ (prefix_algo) + 2; /* 2 for ', ' */
-#ifdef MHD_MD5_SUPPORT
-    if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
-      buf_size += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
-    else
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-    if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
-      buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN);
-    else
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-    if (MHD_DIGEST_AUTH_ALGO3_SHA512_256 == s_algo)
-      buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA512_256_TOKEN);
-    else
-#endif /* MHD_SHA512_256_SUPPORT */
-    mhd_assert (0);
-  }
-  /* 'nonce="xxxx", ' */
-  buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */
-  buf_size += NONCE_STD_LEN (digest_get_size (da)); /* Escaping not needed */
-  /* 'opaque="xxxx", ' */
-  if (NULL != opaque)
-  {
-    buf_size += MHD_STATICSTR_LEN_ (prefix_opaque) + 3; /* 3 for '", ' */
-    opaque_len = strlen (opaque);
-    if ((NULL != memchr (opaque, '\r', opaque_len)) ||
-        (NULL != memchr (opaque, '\n', opaque_len)))
-      return MHD_NO;
-
-    buf_size += opaque_len * 2; /* Quoting may double the size */
-  }
-  else
-    opaque_len = 0;
-  /* 'domain="xxxx", ' */
-  if (NULL != domain)
-  {
-    buf_size += MHD_STATICSTR_LEN_ (prefix_domain) + 3; /* 3 for '", ' */
-    domain_len = strlen (domain);
-    if ((NULL != memchr (domain, '\r', domain_len)) ||
-        (NULL != memchr (domain, '\n', domain_len)))
-      return MHD_NO;
-
-    buf_size += domain_len * 2; /* Quoting may double the size */
-  }
-  else
-    domain_len = 0;
-  /* 'charset=UTF-8' */
-  if (MHD_NO != prefer_utf8)
-    buf_size += MHD_STATICSTR_LEN_ (str_charset) + 2; /* 2 for ', ' */
-  /* 'userhash=true' */
-  if (MHD_NO != userhash_support)
-    buf_size += MHD_STATICSTR_LEN_ (str_userhash) + 2; /* 2 for ', ' */
-  /* 'stale=true' */
-  if (MHD_NO != signal_stale)
-    buf_size += MHD_STATICSTR_LEN_ (str_stale) + 2; /* 2 for ', ' */
-
-  /* The calculated length is for string ended with ", ". One character will
-   * be used for zero-termination, the last one will not be used. */
-
-  /* Allocate the buffer */
-  buf = malloc (buf_size);
-  if (NULL == buf)
-    return MHD_NO;
-  *buf_ptr = buf;
-
-  /* Build the challenge string */
-  p = 0;
-  /* 'Digest: ' */
-  memcpy (buf + p, _MHD_AUTH_DIGEST_BASE,
-          MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE));
-  p += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE);
-  buf[p++] = ' ';
-  /* 'realm="xxxx", ' */
-  memcpy (buf + p, prefix_realm,
-          MHD_STATICSTR_LEN_ (prefix_realm));
-  p += MHD_STATICSTR_LEN_ (prefix_realm);
-  mhd_assert ((buf_size - p) >= (realm_len * 2));
-  if (1)
-  {
-    size_t quoted_size;
-    quoted_size = MHD_str_quote (realm, realm_len, buf + p, buf_size - p);
-    if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < quoted_size)
-    {
-#ifdef HAVE_MESSAGES
-      MHD_DLOG (connection->daemon,
-                _ ("The 'realm' is too large after 'quoting'.\n"));
-#endif /* HAVE_MESSAGES */
-      return MHD_NO;
-    }
-    p += quoted_size;
-  }
-  buf[p++] = '\"';
-  buf[p++] = ',';
-  buf[p++] = ' ';
-  /* 'qop="xxxx", ' */
-  if (MHD_DIGEST_AUTH_MULT_QOP_NONE != mqop)
-  {
-    memcpy (buf + p, prefix_qop,
-            MHD_STATICSTR_LEN_ (prefix_qop));
-    p += MHD_STATICSTR_LEN_ (prefix_qop);
-    memcpy (buf + p, MHD_TOKEN_AUTH_,
-            MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_));
-    p += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_);
-    buf[p++] = '\"';
-    buf[p++] = ',';
-    buf[p++] = ' ';
-  }
-  /* 'algorithm="xxxx", ' */
-  if (((MHD_DIGEST_AUTH_MULT_QOP_NONE) != mqop) ||
-      (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5)))
-  {
-    memcpy (buf + p, prefix_algo,
-            MHD_STATICSTR_LEN_ (prefix_algo));
-    p += MHD_STATICSTR_LEN_ (prefix_algo);
-#ifdef MHD_MD5_SUPPORT
-    if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
-    {
-      memcpy (buf + p, _MHD_MD5_TOKEN,
-              MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN));
-      p += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
-    }
-    else
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-    if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
-    {
-      memcpy (buf + p, _MHD_SHA256_TOKEN,
-              MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN));
-      p += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN);
-    }
-    else
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-    if (MHD_DIGEST_AUTH_ALGO3_SHA512_256 == s_algo)
-    {
-      memcpy (buf + p, _MHD_SHA512_256_TOKEN,
-              MHD_STATICSTR_LEN_ (_MHD_SHA512_256_TOKEN));
-      p += MHD_STATICSTR_LEN_ (_MHD_SHA512_256_TOKEN);
-    }
-    else
-#endif /* MHD_SHA512_256_SUPPORT */
-    mhd_assert (0);
-    buf[p++] = ',';
-    buf[p++] = ' ';
-  }
-  /* 'nonce="xxxx", ' */
-  memcpy (buf + p, prefix_nonce,
-          MHD_STATICSTR_LEN_ (prefix_nonce));
-  p += MHD_STATICSTR_LEN_ (prefix_nonce);
-  mhd_assert ((buf_size - p) >= (NONCE_STD_LEN (digest_get_size (da))));
-  if (! calculate_add_nonce_with_retry (connection, realm, da, buf + p))
-  {
-#ifdef MHD_DIGEST_HAS_EXT_ERROR
-    if (digest_ext_error (da))
-    {
-#ifdef HAVE_MESSAGES
-      MHD_DLOG (connection->daemon,
-                _ ("TLS library reported hash calculation error, nonce could "
-                   "not be generated.\n"));
-#endif /* HAVE_MESSAGES */
-      return MHD_NO;
-    }
-#endif /* MHD_DIGEST_HAS_EXT_ERROR */
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("Could not register nonce. Client's requests with this "
-                 "nonce will be always 'stale'. Probably clients' requests "
-                 "are too intensive.\n"));
-#endif /* HAVE_MESSAGES */
-    (void) 0; /* Mute compiler warning for builds without messages */
-  }
-  p += NONCE_STD_LEN (digest_get_size (da));
-  buf[p++] = '\"';
-  buf[p++] = ',';
-  buf[p++] = ' ';
-  /* 'opaque="xxxx", ' */
-  if (NULL != opaque)
-  {
-    memcpy (buf + p, prefix_opaque,
-            MHD_STATICSTR_LEN_ (prefix_opaque));
-    p += MHD_STATICSTR_LEN_ (prefix_opaque);
-    mhd_assert ((buf_size - p) >= (opaque_len * 2));
-    p += MHD_str_quote (opaque, opaque_len, buf + p, buf_size - p);
-    buf[p++] = '\"';
-    buf[p++] = ',';
-    buf[p++] = ' ';
-  }
-  /* 'domain="xxxx", ' */
-  if (NULL != domain)
-  {
-    memcpy (buf + p, prefix_domain,
-            MHD_STATICSTR_LEN_ (prefix_domain));
-    p += MHD_STATICSTR_LEN_ (prefix_domain);
-    mhd_assert ((buf_size - p) >= (domain_len * 2));
-    p += MHD_str_quote (domain, domain_len, buf + p, buf_size - p);
-    buf[p++] = '\"';
-    buf[p++] = ',';
-    buf[p++] = ' ';
-  }
-  /* 'charset=UTF-8' */
-  if (MHD_NO != prefer_utf8)
-  {
-    memcpy (buf + p, str_charset,
-            MHD_STATICSTR_LEN_ (str_charset));
-    p += MHD_STATICSTR_LEN_ (str_charset);
-    buf[p++] = ',';
-    buf[p++] = ' ';
-  }
-  /* 'userhash=true' */
-  if (MHD_NO != userhash_support)
-  {
-    memcpy (buf + p, str_userhash,
-            MHD_STATICSTR_LEN_ (str_userhash));
-    p += MHD_STATICSTR_LEN_ (str_userhash);
-    buf[p++] = ',';
-    buf[p++] = ' ';
-  }
-  /* 'stale=true' */
-  if (MHD_NO != signal_stale)
-  {
-    memcpy (buf + p, str_stale,
-            MHD_STATICSTR_LEN_ (str_stale));
-    p += MHD_STATICSTR_LEN_ (str_stale);
-    buf[p++] = ',';
-    buf[p++] = ' ';
-  }
-  mhd_assert (buf_size >= p);
-  /* The built string ends with ", ". Replace comma with zero-termination. */
-  --p;
-  buf[--p] = 0;
-
-  hdr_name = malloc (MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_WWW_AUTHENTICATE) + 1);
-  if (NULL != hdr_name)
-  {
-    memcpy (hdr_name, MHD_HTTP_HEADER_WWW_AUTHENTICATE,
-            MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_WWW_AUTHENTICATE) + 1);
-    if (MHD_add_response_entry_no_alloc_ (response, MHD_HEADER_KIND,
-                                          hdr_name,
-                                          MHD_STATICSTR_LEN_ ( \
-                                            MHD_HTTP_HEADER_WWW_AUTHENTICATE),
-                                          buf, p))
-    {
-      *buf_ptr = NULL; /* The buffer will be free()ed when the response is destroyed */
-      return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
-    }
-#ifdef HAVE_MESSAGES
-    else
-    {
-      MHD_DLOG (connection->daemon,
-                _ ("Failed to add Digest auth header.\n"));
-    }
-#endif /* HAVE_MESSAGES */
-    free (hdr_name);
-  }
-  return MHD_NO;
-}
-
-
-/**
- * Queues a response to request authentication from the client
- *
- * This function modifies provided @a response. The @a response must not be
- * reused and should be destroyed (by #MHD_destroy_response()) after call of
- * this function.
- *
- * If @a mqop allows both RFC 2069 (MHD_DIGEST_AUTH_QOP_NONE) and QOP with
- * value, then response is formed like if MHD_DIGEST_AUTH_QOP_NONE bit was
- * not set, because such response should be backward-compatible with RFC 2069.
- *
- * If @a mqop allows only MHD_DIGEST_AUTH_MULT_QOP_NONE, then the response is
- * formed in strict accordance with RFC 2069 (no 'qop', no 'userhash', no
- * 'charset'). For better compatibility with clients, it is recommended (but
- * not required) to set @a domain to NULL in this mode.
- *
- * @param connection the MHD connection structure
- * @param realm the realm presented to the client
- * @param opaque the string for opaque value, can be NULL, but NULL is
- *               not recommended for better compatibility with clients;
- *               the recommended format is hex or Base64 encoded string
- * @param domain the optional space-separated list of URIs for which the
- *               same authorisation could be used, URIs can be in form
- *               "path-absolute" (the path for the same host with initial slash)
- *               or in form "absolute-URI" (the full path with protocol), in
- *               any case client may assume that URI is in the same "protection
- *               space" if it starts with any of values specified here;
- *               could be NULL (clients typically assume that the same
- *               credentials could be used for any URI on the same host);
- *               this list provides information for the client only and does
- *               not actually restrict anything on the server side
- * @param response the reply to send; should contain the "access denied"
- *                 body;
- *                 note: this function sets the "WWW Authenticate" header and
- *                 the caller should not set this header;
- *                 the NULL is tolerated
- * @param signal_stale if set to #MHD_YES then indication of stale nonce used in
- *                     the client's request is signalled by adding 'stale=true'
- *                     to the authentication header, this instructs the client
- *                     to retry immediately with the new nonce and the same
- *                     credentials, without asking user for the new password
- * @param mqop the QOP to use
- * @param malgo3 digest algorithm to use; if several algorithms are allowed
- *               then MD5 is preferred (currently, may be changed in next
- *               versions)
- * @param userhash_support if set to non-zero value (#MHD_YES) then support of
- *                         userhash is indicated, allowing client to provide
- *                         hash("username:realm") instead of the username in
- *                         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 for
- *                    the username is preferred
- * @return #MHD_YES on success, #MHD_NO otherwise
- * @note Available since #MHD_VERSION 0x00097701
- * @ingroup authentication
- */
-_MHD_EXTERN enum MHD_Result
-MHD_queue_auth_required_response3 (struct MHD_Connection *connection,
-                                   const char *realm,
-                                   const char *opaque,
-                                   const char *domain,
-                                   struct MHD_Response *response,
-                                   int signal_stale,
-                                   enum MHD_DigestAuthMultiQOP mqop,
-                                   enum MHD_DigestAuthMultiAlgo3 malgo3,
-                                   int userhash_support,
-                                   int prefer_utf8)
-{
-  struct DigestAlgorithm da;
-  char *buf_ptr;
-  enum MHD_Result ret;
-
-  buf_ptr = NULL;
-  digest_setup_zero (&da);
-  ret = queue_auth_required_response3_inner (connection,
-                                             realm,
-                                             opaque,
-                                             domain,
-                                             response,
-                                             signal_stale,
-                                             mqop,
-                                             malgo3,
-                                             userhash_support,
-                                             prefer_utf8,
-                                             &buf_ptr,
-                                             &da);
-  digest_deinit (&da);
-  if (NULL != buf_ptr)
-    free (buf_ptr);
-  return ret;
-}
-
-
-/**
- * Queues a response to request authentication from the client
- *
- * @param connection The MHD connection structure
- * @param realm the realm presented to the client
- * @param opaque string to user for opaque value
- * @param response reply to send; should contain the "access denied"
- *        body; note that this function will set the "WWW Authenticate"
- *        header and that the caller should not do this; the NULL is tolerated
- * @param signal_stale #MHD_YES if the nonce is stale to add
- *        'stale=true' to the authentication header
- * @param algo digest algorithm to use
- * @return #MHD_YES on success, #MHD_NO otherwise
- * @note Available since #MHD_VERSION 0x00096200
- * @ingroup authentication
- */
-_MHD_EXTERN enum MHD_Result
-MHD_queue_auth_fail_response2 (struct MHD_Connection *connection,
-                               const char *realm,
-                               const char *opaque,
-                               struct MHD_Response *response,
-                               int signal_stale,
-                               enum MHD_DigestAuthAlgorithm algo)
-{
-  enum MHD_DigestAuthMultiAlgo3 algo3;
-
-  if (MHD_DIGEST_ALG_MD5 == algo)
-    algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5;
-  else if (MHD_DIGEST_ALG_SHA256 == algo)
-    algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
-  else if (MHD_DIGEST_ALG_AUTO == algo)
-    algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
-  else
-    MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */
-
-  return MHD_queue_auth_required_response3 (connection, realm, opaque,
-                                            NULL, response, signal_stale,
-                                            MHD_DIGEST_AUTH_MULT_QOP_AUTH,
-                                            algo3,
-                                            0, 0);
-}
-
-
-/**
- * Queues a response to request authentication from the client.
- * For now uses MD5 (for backwards-compatibility). Still, if you
- * need to be sure, use #MHD_queue_auth_fail_response2().
- *
- * @param connection The MHD connection structure
- * @param realm the realm presented to the client
- * @param opaque string to user for opaque value
- * @param response reply to send; should contain the "access denied"
- *        body; note that this function will set the "WWW Authenticate"
- *        header and that the caller should not do this; the NULL is tolerated
- * @param signal_stale #MHD_YES if the nonce is stale to add
- *        'stale=true' to the authentication header
- * @return #MHD_YES on success, #MHD_NO otherwise
- * @ingroup authentication
- * @deprecated use MHD_queue_auth_fail_response2()
- */
-_MHD_EXTERN enum MHD_Result
-MHD_queue_auth_fail_response (struct MHD_Connection *connection,
-                              const char *realm,
-                              const char *opaque,
-                              struct MHD_Response *response,
-                              int signal_stale)
-{
-  return MHD_queue_auth_fail_response2 (connection,
-                                        realm,
-                                        opaque,
-                                        response,
-                                        signal_stale,
-                                        MHD_DIGEST_ALG_MD5);
-}
-
-
-/* end of digestauth.c */

+ 0 - 84
src/microhttpd/digestauth.h

@@ -1,84 +0,0 @@
-/*
-     This file is part of libmicrohttpd
-     Copyright (C) 2010, 2011, 2012, 2015, 2018 Daniel Pittman and Christian Grothoff
-     Copyright (C) 2014-2022 Evgeny Grin (Karlson2k)
-
-     This library is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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 digestauth.c
- * @brief Implements HTTP digest authentication
- * @author Amr Ali
- * @author Matthieu Speder
- * @author Christian Grothoff (RFC 7616 support)
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_DIGESTAUTH_H
-#define MHD_DIGESTAUTH_H 1
-
-#include "mhd_options.h"
-#include "mhd_str.h"
-#ifdef HAVE_STDBOOL_H
-#include <stdbool.h>
-#endif /* HAVE_STDBOOL_H */
-
-
-/**
- * The maximum supported size for Digest Auth parameters, like "real",
- * "username" etc. This limitation is used only for quoted parameters.
- * Parameters without quoted backslash character will be processed as long
- * as they fit connection pool (buffer) size.
- */
-#define _MHD_AUTH_DIGEST_MAX_PARAM_SIZE (65535)
-
-/**
- * Beginning string for any valid Digest Authentication header.
- */
-#define _MHD_AUTH_DIGEST_BASE   "Digest"
-
-/**
- * The token for MD5 algorithm.
- */
-#define _MHD_MD5_TOKEN "MD5"
-
-/**
- * The token for SHA-256 algorithm.
- */
-#define _MHD_SHA256_TOKEN "SHA-256"
-
-/**
- * The token for SHA-512/256 algorithm.
- */
-#define _MHD_SHA512_256_TOKEN "SHA-512-256"
-
-/**
- * The suffix token for "session" algorithms.
- */
-#define _MHD_SESS_TOKEN "-sess"
-
-/**
- * The "auth" token for QOP
- */
-#define MHD_TOKEN_AUTH_ "auth"
-
-/**
- * The "auth-int" token for QOP
- */
-#define MHD_TOKEN_AUTH_INT_ "auth-int"
-
-#endif /* ! MHD_DIGESTAUTH_H */
-
-/* end of digestauth.h */

+ 0 - 682
src/microhttpd/gen_auth.c

@@ -1,682 +0,0 @@
-/*
-  This file is part of libmicrohttpd
-  Copyright (C) 2022-2023 Evgeny Grin (Karlson2k)
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library 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, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file microhttpd/gen_auth.c
- * @brief  HTTP authorisation general functions
- * @author Karlson2k (Evgeny Grin)
- */
-
-#include "gen_auth.h"
-#include "internal.h"
-#include "connection.h"
-#include "mhd_str.h"
-#include "mhd_assert.h"
-
-#ifdef BAUTH_SUPPORT
-#include "basicauth.h"
-#endif /* BAUTH_SUPPORT */
-#ifdef DAUTH_SUPPORT
-#include "digestauth.h"
-#endif /* DAUTH_SUPPORT */
-
-#if ! defined(BAUTH_SUPPORT) && ! defined(DAUTH_SUPPORT)
-#error This file requires Basic or Digest authentication support
-#endif
-
-/**
- * Type of authorisation
- */
-enum MHD_AuthType
-{
-  MHD_AUTHTYPE_NONE = 0,/**< No authorisation, unused */
-  MHD_AUTHTYPE_BASIC,   /**< Basic Authorisation, RFC 7617  */
-  MHD_AUTHTYPE_DIGEST,  /**< Digest Authorisation, RFC 7616 */
-  MHD_AUTHTYPE_UNKNOWN, /**< Unknown/Unsupported authorisation type, unused */
-  MHD_AUTHTYPE_INVALID  /**< Wrong/broken authorisation header, unused */
-};
-
-/**
- * Find required "Authorization" request header
- * @param c the connection with request
- * @param type the type of the authorisation: basic or digest
- * @param[out] auth_value will be set to the remaining of header value after
- *                        authorisation token (after "Basic " or "Digest ")
- * @return true if requested header is found,
- *         false otherwise
- */
-static bool
-find_auth_rq_header_ (const struct MHD_Connection *c, enum MHD_AuthType type,
-                      struct _MHD_str_w_len *auth_value)
-{
-  const struct MHD_HTTP_Req_Header *h;
-  const char *token;
-  size_t token_len;
-
-  mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= c->state);
-  if (MHD_CONNECTION_HEADERS_PROCESSED > c->state)
-    return false;
-
-#ifdef DAUTH_SUPPORT
-  if (MHD_AUTHTYPE_DIGEST == type)
-  {
-    token = _MHD_AUTH_DIGEST_BASE;
-    token_len = MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE);
-  }
-  else /* combined with the next line */
-#endif /* DAUTH_SUPPORT */
-#ifdef BAUTH_SUPPORT
-  if (MHD_AUTHTYPE_BASIC == type)
-  {
-    token = _MHD_AUTH_BASIC_BASE;
-    token_len = MHD_STATICSTR_LEN_ (_MHD_AUTH_BASIC_BASE);
-  }
-  else /* combined with the next line */
-#endif /* BAUTH_SUPPORT */
-  {
-    mhd_assert (0);
-    return false;
-  }
-
-  for (h = c->rq.headers_received; NULL != h; h = h->next)
-  {
-    if (MHD_HEADER_KIND != h->kind)
-      continue;
-    if (MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_AUTHORIZATION) != h->header_size)
-      continue;
-    if (token_len > h->value_size)
-      continue;
-    if (! MHD_str_equal_caseless_bin_n_ (MHD_HTTP_HEADER_AUTHORIZATION,
-                                         h->header,
-                                         MHD_STATICSTR_LEN_ ( \
-                                           MHD_HTTP_HEADER_AUTHORIZATION)))
-      continue;
-    if (! MHD_str_equal_caseless_bin_n_ (h->value, token, token_len))
-      continue;
-    /* Match only if token string is full header value or token is
-     * followed by space or tab
-     * Note: RFC 9110 (and RFC 7234) allows only space character, but
-     * tab is supported here as well for additional flexibility and uniformity
-     * as tabs are supported as separators between parameters.
-     */
-    if ((token_len == h->value_size) ||
-        (' ' == h->value[token_len]) || ('\t'  == h->value[token_len]))
-    {
-      if (token_len != h->value_size)
-      { /* Skip whitespace */
-        auth_value->str = h->value + token_len + 1;
-        auth_value->len = h->value_size - (token_len + 1);
-      }
-      else
-      { /* No whitespace to skip */
-        auth_value->str = h->value + token_len;
-        auth_value->len = h->value_size - token_len;
-      }
-      return true; /* Found a match */
-    }
-  }
-  return false; /* No matching header has been found */
-}
-
-
-#ifdef BAUTH_SUPPORT
-
-
-/**
- * Parse request Authorization header parameters for Basic Authentication
- * @param str the header string, everything after "Basic " substring
- * @param str_len the length of @a str in characters
- * @param[out] pbauth the pointer to the structure with Basic Authentication
- *               parameters
- * @return true if parameters has been successfully parsed,
- *         false if format of the @a str is invalid
- */
-static bool
-parse_bauth_params (const char *str,
-                    size_t str_len,
-                    struct MHD_RqBAuth *pbauth)
-{
-  size_t i;
-
-  i = 0;
-
-  /* Skip all whitespaces at start */
-  while (i < str_len && (' ' == str[i] || '\t' == str[i]))
-    i++;
-
-  if (str_len > i)
-  {
-    size_t token68_start;
-    size_t token68_len;
-
-    /* 'i' points to the first non-whitespace char after scheme token */
-    token68_start = i;
-    /* Find end of the token. Token cannot contain whitespace. */
-    while (i < str_len && ' ' != str[i] && '\t' != str[i])
-    {
-      if (0 == str[i])
-        return false;  /* Binary zero is not allowed */
-      if ((',' == str[i]) || (';' == str[i]))
-        return false;  /* Only single token68 is allowed */
-      i++;
-    }
-    token68_len = i - token68_start;
-    mhd_assert (0 != token68_len);
-
-    /* Skip all whitespaces */
-    while (i < str_len && (' ' == str[i] || '\t' == str[i]))
-      i++;
-    /* Check whether any garbage is present at the end of the string */
-    if (str_len != i)
-      return false;
-    else
-    {
-      /* No more data in the string, only single token68. */
-      pbauth->token68.str = str + token68_start;
-      pbauth->token68.len = token68_len;
-    }
-  }
-  return true;
-}
-
-
-/**
- * Return request's Basic Authorisation parameters.
- *
- * Function return result of parsing of the request's "Authorization" header or
- * returns cached parsing result if the header was already parsed for
- * the current request.
- * @param connection the connection to process
- * @return the pointer to structure with Authentication parameters,
- *         NULL if no memory in memory pool, if called too early (before
- *         header has been received) or if no valid Basic Authorisation header
- *         found.
- */
-const struct MHD_RqBAuth *
-MHD_get_rq_bauth_params_ (struct MHD_Connection *connection)
-{
-  struct _MHD_str_w_len h_auth_value;
-  struct MHD_RqBAuth *bauth;
-
-  mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= connection->state);
-
-  if (connection->rq.bauth_tried)
-    return connection->rq.bauth;
-
-  if (MHD_CONNECTION_HEADERS_PROCESSED > connection->state)
-    return NULL;
-
-  if (! find_auth_rq_header_ (connection, MHD_AUTHTYPE_BASIC, &h_auth_value))
-  {
-    connection->rq.bauth_tried = true;
-    connection->rq.bauth = NULL;
-    return NULL;
-  }
-
-  bauth =
-    (struct MHD_RqBAuth *)
-    MHD_connection_alloc_memory_ (connection, sizeof (struct MHD_RqBAuth));
-
-  if (NULL == bauth)
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("Not enough memory in the connection's pool to allocate " \
-                 "for Basic Authorization header parsing.\n"));
-#endif /* HAVE_MESSAGES */
-    return NULL;
-  }
-
-  memset (bauth, 0, sizeof(struct MHD_RqBAuth));
-  if (parse_bauth_params (h_auth_value.str, h_auth_value.len, bauth))
-    connection->rq.bauth = bauth;
-  else
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The Basic Authorization client's header has "
-                 "incorrect format.\n"));
-#endif /* HAVE_MESSAGES */
-    connection->rq.bauth = NULL;
-    /* Memory in the pool remains allocated until next request */
-  }
-  connection->rq.bauth_tried = true;
-  return connection->rq.bauth;
-}
-
-
-#endif /* BAUTH_SUPPORT */
-
-#ifdef DAUTH_SUPPORT
-
-
-/**
- * Get client's Digest Authorization algorithm type.
- * If no algorithm is specified by client, MD5 is assumed.
- * @param params the Digest Authorization 'algorithm' parameter
- * @return the algorithm type
- */
-static enum MHD_DigestAuthAlgo3
-get_rq_dauth_algo (const struct MHD_RqDAuthParam *const algo_param)
-{
-  if (NULL == algo_param->value.str)
-    return MHD_DIGEST_AUTH_ALGO3_MD5; /* Assume MD5 by default */
-
-  if (algo_param->quoted)
-  {
-    if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
-                                               algo_param->value.len, \
-                                               _MHD_MD5_TOKEN))
-      return MHD_DIGEST_AUTH_ALGO3_MD5;
-    if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
-                                               algo_param->value.len, \
-                                               _MHD_SHA256_TOKEN))
-      return MHD_DIGEST_AUTH_ALGO3_SHA256;
-    if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
-                                               algo_param->value.len, \
-                                               _MHD_MD5_TOKEN _MHD_SESS_TOKEN))
-      return MHD_DIGEST_AUTH_ALGO3_SHA512_256;
-    if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
-                                               algo_param->value.len, \
-                                               _MHD_SHA512_256_TOKEN \
-                                               _MHD_SESS_TOKEN))
-
-      /* Algorithms below are not supported by MHD for authentication */
-
-      return MHD_DIGEST_AUTH_ALGO3_MD5_SESSION;
-    if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
-                                               algo_param->value.len, \
-                                               _MHD_SHA256_TOKEN \
-                                               _MHD_SESS_TOKEN))
-      return MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION;
-    if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
-                                               algo_param->value.len, \
-                                               _MHD_SHA512_256_TOKEN))
-      return MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION;
-
-    /* No known algorithm has been detected */
-    return MHD_DIGEST_AUTH_ALGO3_INVALID;
-  }
-  /* The algorithm value is not quoted */
-  if (MHD_str_equal_caseless_s_bin_n_ (_MHD_MD5_TOKEN, \
-                                       algo_param->value.str, \
-                                       algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_MD5;
-  if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA256_TOKEN, \
-                                       algo_param->value.str, \
-                                       algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_SHA256;
-  if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA512_256_TOKEN, \
-                                       algo_param->value.str, \
-                                       algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_SHA512_256;
-
-  /* Algorithms below are not supported by MHD for authentication */
-
-  if (MHD_str_equal_caseless_s_bin_n_ (_MHD_MD5_TOKEN _MHD_SESS_TOKEN, \
-                                       algo_param->value.str, \
-                                       algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_MD5_SESSION;
-  if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA256_TOKEN _MHD_SESS_TOKEN, \
-                                       algo_param->value.str, \
-                                       algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION;
-  if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA512_256_TOKEN _MHD_SESS_TOKEN, \
-                                       algo_param->value.str, \
-                                       algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION;
-
-  /* No known algorithm has been detected */
-  return MHD_DIGEST_AUTH_ALGO3_INVALID;
-}
-
-
-/**
- * Get QOP ('quality of protection') type.
- * @param qop_param the Digest Authorization 'QOP' parameter
- * @return detected QOP ('quality of protection') type.
- */
-static enum MHD_DigestAuthQOP
-get_rq_dauth_qop (const struct MHD_RqDAuthParam *const qop_param)
-{
-  if (NULL == qop_param->value.str)
-    return MHD_DIGEST_AUTH_QOP_NONE;
-  if (qop_param->quoted)
-  {
-    if (MHD_str_equal_caseless_quoted_s_bin_n (qop_param->value.str, \
-                                               qop_param->value.len, \
-                                               MHD_TOKEN_AUTH_))
-      return MHD_DIGEST_AUTH_QOP_AUTH;
-    if (MHD_str_equal_caseless_quoted_s_bin_n (qop_param->value.str, \
-                                               qop_param->value.len, \
-                                               MHD_TOKEN_AUTH_INT_))
-      return MHD_DIGEST_AUTH_QOP_AUTH_INT;
-  }
-  else
-  {
-    if (MHD_str_equal_caseless_s_bin_n_ (MHD_TOKEN_AUTH_, \
-                                         qop_param->value.str, \
-                                         qop_param->value.len))
-      return MHD_DIGEST_AUTH_QOP_AUTH;
-    if (MHD_str_equal_caseless_s_bin_n_ (MHD_TOKEN_AUTH_INT_, \
-                                         qop_param->value.str, \
-                                         qop_param->value.len))
-      return MHD_DIGEST_AUTH_QOP_AUTH_INT;
-  }
-  /* No know QOP has been detected */
-  return MHD_DIGEST_AUTH_QOP_INVALID;
-}
-
-
-/**
- * Parse request Authorization header parameters for Digest Authentication
- * @param str the header string, everything after "Digest " substring
- * @param str_len the length of @a str in characters
- * @param[out] pdauth the pointer to the structure with Digest Authentication
- *               parameters
- * @return true if parameters has been successfully parsed,
- *         false if format of the @a str is invalid
- */
-static bool
-parse_dauth_params (const char *str,
-                    const size_t str_len,
-                    struct MHD_RqDAuth *pdauth)
-{
-  /* The tokens */
-  static const struct _MHD_cstr_w_len nonce_tk = _MHD_S_STR_W_LEN ("nonce");
-  static const struct _MHD_cstr_w_len opaque_tk = _MHD_S_STR_W_LEN ("opaque");
-  static const struct _MHD_cstr_w_len algorithm_tk =
-    _MHD_S_STR_W_LEN ("algorithm");
-  static const struct _MHD_cstr_w_len response_tk =
-    _MHD_S_STR_W_LEN ("response");
-  static const struct _MHD_cstr_w_len username_tk =
-    _MHD_S_STR_W_LEN ("username");
-  static const struct _MHD_cstr_w_len username_ext_tk =
-    _MHD_S_STR_W_LEN ("username*");
-  static const struct _MHD_cstr_w_len realm_tk = _MHD_S_STR_W_LEN ("realm");
-  static const struct _MHD_cstr_w_len uri_tk = _MHD_S_STR_W_LEN ("uri");
-  static const struct _MHD_cstr_w_len qop_tk = _MHD_S_STR_W_LEN ("qop");
-  static const struct _MHD_cstr_w_len cnonce_tk = _MHD_S_STR_W_LEN ("cnonce");
-  static const struct _MHD_cstr_w_len nc_tk = _MHD_S_STR_W_LEN ("nc");
-  static const struct _MHD_cstr_w_len userhash_tk =
-    _MHD_S_STR_W_LEN ("userhash");
-  /* The locally processed parameters */
-  struct MHD_RqDAuthParam userhash;
-  struct MHD_RqDAuthParam algorithm;
-  /* Indexes */
-  size_t i;
-  size_t p;
-  /* The list of the tokens.
-     The order of the elements matches the next array. */
-  static const struct _MHD_cstr_w_len *const tk_names[] = {
-    &nonce_tk,          /* 0 */
-    &opaque_tk,         /* 1 */
-    &algorithm_tk,      /* 2 */
-    &response_tk,       /* 3 */
-    &username_tk,       /* 4 */
-    &username_ext_tk,   /* 5 */
-    &realm_tk,          /* 6 */
-    &uri_tk,            /* 7 */
-    &qop_tk,            /* 8 */
-    &cnonce_tk,         /* 9 */
-    &nc_tk,             /* 10 */
-    &userhash_tk        /* 11 */
-  };
-  /* The list of the parameters.
-     The order of the elements matches the previous array. */
-  struct MHD_RqDAuthParam *params[sizeof(tk_names) / sizeof(tk_names[0])];
-
-  params[0 ] = &(pdauth->nonce);           /* 0 */
-  params[1 ] = &(pdauth->opaque);          /* 1 */
-  params[2 ] = &algorithm;                 /* 2 */
-  params[3 ] = &(pdauth->response);        /* 3 */
-  params[4 ] = &(pdauth->username);        /* 4 */
-  params[5 ] = &(pdauth->username_ext);    /* 5 */
-  params[6 ] = &(pdauth->realm);           /* 6 */
-  params[7 ] = &(pdauth->uri);             /* 7 */
-  params[8 ] = &(pdauth->qop_raw);         /* 8 */
-  params[9 ] = &(pdauth->cnonce);          /* 9 */
-  params[10] = &(pdauth->nc);              /* 10 */
-  params[11] = &userhash;                  /* 11 */
-
-  mhd_assert ((sizeof(tk_names) / sizeof(tk_names[0])) == \
-              (sizeof(params) / sizeof(params[0])));
-  memset (&userhash, 0, sizeof(userhash));
-  memset (&algorithm, 0, sizeof(algorithm));
-  i = 0;
-
-  /* Skip all whitespaces at start */
-  while (i < str_len && (' ' == str[i] || '\t' == str[i]))
-    i++;
-
-  while (str_len > i)
-  {
-    size_t left;
-    mhd_assert (' ' != str[i]);
-    mhd_assert ('\t' != str[i]);
-
-    left = str_len - i;
-    if ('=' == str[i])
-      return false; /* The equal sign is not allowed as the first character */
-    for (p = 0; p < (sizeof(tk_names) / sizeof(tk_names[0])); ++p)
-    {
-      const struct _MHD_cstr_w_len *const tk_name = tk_names[p];
-      struct MHD_RqDAuthParam *const param = params[p];
-      if ( (tk_name->len <= left) &&
-           MHD_str_equal_caseless_bin_n_ (str + i, tk_name->str,
-                                          tk_name->len) &&
-           ((tk_name->len == left) ||
-            ('=' == str[i + tk_name->len]) ||
-            (' ' == str[i + tk_name->len]) ||
-            ('\t' == str[i + tk_name->len]) ||
-            (',' == str[i + tk_name->len]) ||
-            (';' == str[i + tk_name->len])) )
-      {
-        size_t value_start;
-        size_t value_len;
-        bool quoted; /* Only mark as "quoted" if backslash-escape used */
-
-        if (tk_name->len == left)
-          return false; /* No equal sign after parameter name, broken data */
-
-        quoted = false;
-        i += tk_name->len;
-        /* Skip all whitespaces before '=' */
-        while (str_len > i && (' ' == str[i] || '\t' == str[i]))
-          i++;
-        if ((i == str_len) || ('=' != str[i]))
-          return false; /* No equal sign, broken data */
-        i++;
-        /* Skip all whitespaces after '=' */
-        while (str_len > i && (' ' == str[i] || '\t' == str[i]))
-          i++;
-        if ((str_len > i) && ('"' == str[i]))
-        { /* Value is in quotation marks */
-          i++; /* Advance after the opening quote */
-          value_start = i;
-          while (str_len > i && '"' != str[i])
-          {
-            if ('\\' == str[i])
-            {
-              i++;
-              quoted = true; /* Have escaped chars */
-            }
-            if (0 == str[i])
-              return false; /* Binary zero in parameter value */
-            i++;
-          }
-          if (str_len <= i)
-            return false; /* No closing quote */
-          mhd_assert ('"' == str[i]);
-          value_len = i - value_start;
-          i++; /* Advance after the closing quote */
-        }
-        else
-        {
-          value_start = i;
-          while (str_len > i && ',' != str[i] &&
-                 ' ' != str[i] && '\t' != str[i] && ';' != str[i])
-          {
-            if (0 == str[i])
-              return false;  /* Binary zero in parameter value */
-            i++;
-          }
-          if (';' == str[i])
-            return false;  /* Semicolon in parameter value */
-          value_len = i - value_start;
-        }
-        /* Skip all whitespaces after parameter value */
-        while (str_len > i && (' ' == str[i] || '\t' == str[i]))
-          i++;
-        if ((str_len > i) && (',' != str[i]))
-          return false; /* Garbage after parameter value */
-
-        /* Have valid parameter name and value */
-        mhd_assert (! quoted || 0 != value_len);
-        param->value.str = str + value_start;
-        param->value.len = value_len;
-        param->quoted = quoted;
-
-        break; /* Found matching parameter name */
-      }
-    }
-    if (p == (sizeof(tk_names) / sizeof(tk_names[0])))
-    {
-      /* No matching parameter name */
-      while (str_len > i && ',' != str[i])
-      {
-        if ((0 == str[i]) || (';' == str[i]))
-          return false; /* Not allowed characters */
-        if ('"' == str[i])
-        { /* Skip quoted part */
-          i++; /* Advance after the opening quote */
-          while (str_len > i && '"' != str[i])
-          {
-            if (0 == str[i])
-              return false;  /* Binary zero is not allowed */
-            if ('\\' == str[i])
-              i++;           /* Skip escaped char */
-            i++;
-          }
-          if (str_len <= i)
-            return false; /* No closing quote */
-          mhd_assert ('"' == str[i]);
-        }
-        i++;
-      }
-    }
-    mhd_assert (str_len == i || ',' == str[i]);
-    if (str_len > i)
-      i++; /* Advance after ',' */
-    /* Skip all whitespaces before next parameter name */
-    while (i < str_len && (' ' == str[i] || '\t' == str[i]))
-      i++;
-  }
-
-  /* Postprocess values */
-
-  if (NULL != userhash.value.str)
-  {
-    if (userhash.quoted)
-      pdauth->userhash =
-        MHD_str_equal_caseless_quoted_s_bin_n (userhash.value.str, \
-                                               userhash.value.len, \
-                                               "true");
-    else
-      pdauth->userhash =
-        MHD_str_equal_caseless_s_bin_n_ ("true", userhash.value.str, \
-                                         userhash.value.len);
-
-  }
-  else
-    pdauth->userhash = false;
-
-  pdauth->algo3 = get_rq_dauth_algo (&algorithm);
-  pdauth->qop = get_rq_dauth_qop (&pdauth->qop_raw);
-
-  return true;
-}
-
-
-/**
- * Return request's Digest Authorisation parameters.
- *
- * Function return result of parsing of the request's "Authorization" header or
- * returns cached parsing result if the header was already parsed for
- * the current request.
- * @param connection the connection to process
- * @return the pointer to structure with Authentication parameters,
- *         NULL if no memory in memory pool, if called too early (before
- *         header has been received) or if no valid Basic Authorisation header
- *         found.
- */
-const struct MHD_RqDAuth *
-MHD_get_rq_dauth_params_ (struct MHD_Connection *connection)
-{
-  struct _MHD_str_w_len h_auth_value;
-  struct MHD_RqDAuth *dauth;
-
-  mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= connection->state);
-
-  if (connection->rq.dauth_tried)
-    return connection->rq.dauth;
-
-  if (MHD_CONNECTION_HEADERS_PROCESSED > connection->state)
-    return NULL;
-
-  if (! find_auth_rq_header_ (connection, MHD_AUTHTYPE_DIGEST, &h_auth_value))
-  {
-    connection->rq.dauth_tried = true;
-    connection->rq.dauth = NULL;
-    return NULL;
-  }
-
-  dauth =
-    (struct MHD_RqDAuth *)
-    MHD_connection_alloc_memory_ (connection, sizeof (struct MHD_RqDAuth));
-
-  if (NULL == dauth)
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("Not enough memory in the connection's pool to allocate " \
-                 "for Digest Authorization header parsing.\n"));
-#endif /* HAVE_MESSAGES */
-    return NULL;
-  }
-
-  memset (dauth, 0, sizeof(struct MHD_RqDAuth));
-  if (parse_dauth_params (h_auth_value.str, h_auth_value.len, dauth))
-    connection->rq.dauth = dauth;
-  else
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (connection->daemon,
-              _ ("The Digest Authorization client's header has "
-                 "incorrect format.\n"));
-#endif /* HAVE_MESSAGES */
-    connection->rq.dauth = NULL;
-    /* Memory in the pool remains allocated until next request */
-  }
-  connection->rq.dauth_tried = true;
-  return connection->rq.dauth;
-}
-
-
-#endif /* DAUTH_SUPPORT */

+ 0 - 76
src/microhttpd/gen_auth.h

@@ -1,76 +0,0 @@
-/*
-  This file is part of libmicrohttpd
-  Copyright (C) 2022 Evgeny Grin (Karlson2k)
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library 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, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file microhttpd/gen_auth.h
- * @brief  Declarations for HTTP authorisation general functions
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_GET_AUTH_H
-#define MHD_GET_AUTH_H 1
-
-#include "mhd_options.h"
-#ifdef HAVE_STDBOOL_H
-#include <stdbool.h>
-#endif /* HAVE_STDBOOL_H */
-
-struct MHD_Connection; /* Forward declaration to avoid include of the large headers */
-
-#ifdef BAUTH_SUPPORT
-
-/* Forward declaration to avoid additional headers inclusion */
-struct MHD_RqBAuth;
-/**
- * Return request's Basic Authorisation parameters.
- *
- * Function return result of parsing of the request's "Authorization" header or
- * returns cached parsing result if the header was already parsed for
- * the current request.
- * @param connection the connection to process
- * @return the pointer to structure with Authentication parameters,
- *         NULL if no memory in memory pool, if called too early (before
- *         header has been received) or if no valid Basic Authorisation header
- *         found.
- */
-const struct MHD_RqBAuth *
-MHD_get_rq_bauth_params_ (struct MHD_Connection *connection);
-
-#endif /* BAUTH_SUPPORT */
-#ifdef DAUTH_SUPPORT
-/* Forward declaration to avoid additional headers inclusion */
-struct MHD_RqDAuth;
-
-/**
- * Return request's Digest Authorisation parameters.
- *
- * Function return result of parsing of the request's "Authorization" header or
- * returns cached parsing result if the header was already parsed for
- * the current request.
- * @param connection the connection to process
- * @return the pointer to structure with Authentication parameters,
- *         NULL if no memory in memory pool, if called too early (before
- *         header has been received) or if no valid Basic Authorisation header
- *         found.
- */
-const struct MHD_RqDAuth *
-MHD_get_rq_dauth_params_ (struct MHD_Connection *connection);
-#endif /* DAUTH_SUPPORT */
-
-#endif /* MHD_GET_AUTH_H */

+ 0 - 131
src/microhttpd/md5.h

@@ -1,131 +0,0 @@
-/*
-     This file is part of GNU libmicrohttpd
-     Copyright (C) 2022 Evgeny Grin (Karlson2k)
-
-     GNU libmicrohttpd is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file microhttpd/md5.h
- * @brief  Calculation of MD5 digest
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_MD5_H
-#define MHD_MD5_H 1
-
-#include "mhd_options.h"
-#include <stdint.h>
-#ifdef HAVE_STDDEF_H
-#include <stddef.h>  /* for size_t */
-#endif /* HAVE_STDDEF_H */
-
-/**
- * Number of bits in single MD5 word.
- */
-#define MD5_WORD_SIZE_BITS 32
-
-/**
- * Number of bytes in single MD5 word.
- */
-#define MD5_BYTES_IN_WORD (MD5_WORD_SIZE_BITS / 8)
-
-/**
- * Hash is kept internally as four 32-bit words.
- * This is intermediate hash size, used during computing the final digest.
- */
-#define MD5_HASH_SIZE_WORDS 4
-
-/**
- * Size of MD5 resulting digest in bytes.
- * This is the final digest size, not intermediate hash.
- */
-#define MD5_DIGEST_SIZE_WORDS MD5_HASH_SIZE_WORDS
-
-/**
- * Size of MD5 resulting digest in bytes
- * This is the final digest size, not intermediate hash.
- */
-#define MD5_DIGEST_SIZE (MD5_DIGEST_SIZE_WORDS * MD5_BYTES_IN_WORD)
-
-/**
- * Size of MD5 digest string in chars including termination NUL.
- */
-#define MD5_DIGEST_STRING_SIZE ((MD5_DIGEST_SIZE) * 2 + 1)
-
-/**
- * Size of MD5 single processing block in bits.
- */
-#define MD5_BLOCK_SIZE_BITS 512
-
-/**
- * Size of MD5 single processing block in bytes.
- */
-#define MD5_BLOCK_SIZE (MD5_BLOCK_SIZE_BITS / 8)
-
-/**
- * Size of MD5 single processing block in words.
- */
-#define MD5_BLOCK_SIZE_WORDS (MD5_BLOCK_SIZE_BITS / MD5_WORD_SIZE_BITS)
-
-
-/**
- * MD5 calculation context
- */
-struct Md5Ctx
-{
-  uint32_t H[MD5_HASH_SIZE_WORDS];         /**< Intermediate hash value / digest at end of calculation */
-  uint32_t buffer[MD5_BLOCK_SIZE_WORDS];   /**< MD5 input data buffer */
-  uint64_t count;                          /**< number of bytes, mod 2^64 */
-};
-
-/**
- * Initialise structure for MD5 calculation.
- *
- * @param ctx the calculation context
- */
-void
-MHD_MD5_init (struct Md5Ctx *ctx);
-
-
-/**
- * MD5 process portion of bytes.
- *
- * @param ctx the calculation context
- * @param data bytes to add to hash
- * @param length number of bytes in @a data
- */
-void
-MHD_MD5_update (struct Md5Ctx *ctx,
-                const uint8_t *data,
-                size_t length);
-
-
-/**
- * Finalise MD5 calculation, return digest.
- *
- * @param ctx the calculation context
- * @param[out] digest set to the hash, must be #MD5_DIGEST_SIZE bytes
- */
-void
-MHD_MD5_finish (struct Md5Ctx *ctx,
-                uint8_t digest[MD5_DIGEST_SIZE]);
-
-/**
- * Indicates that function MHD_MD5_finish() (without context reset) is available
- */
-#define MHD_MD5_HAS_FINISH 1
-
-#endif /* MHD_MD5_H */

+ 0 - 113
src/microhttpd/md5_ext.h

@@ -1,113 +0,0 @@
-/*
-     This file is part of GNU libmicrohttpd
-     Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
-
-     GNU libmicrohttpd is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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 GNU libmicrohttpd.
-     If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @file microhttpd/md5_ext.h
- * @brief  Wrapper declarations for MD5 calculation performed by TLS library
- * @author Karlson2k (Evgeny Grin)
- */
-#ifndef MHD_MD5_EXT_H
-#define MHD_MD5_EXT_H 1
-
-#include "mhd_options.h"
-#include <stdint.h>
-#ifdef HAVE_STDDEF_H
-#include <stddef.h>  /* for size_t */
-#endif /* HAVE_STDDEF_H */
-
-/**
- * Size of MD5 resulting digest in bytes
- * This is the final digest size, not intermediate hash.
- */
-#define MD5_DIGEST_SIZE (16)
-
-/* Actual declaration is in GnuTLS lib header */
-struct hash_hd_st;
-
-/**
- * Indicates that struct Md5CtxExt has 'ext_error'
- */
-#define MHD_MD5_HAS_EXT_ERROR 1
-
-/**
- * MD5 calculation context
- */
-struct Md5CtxExt
-{
-  struct hash_hd_st *handle; /**< Hash calculation handle */
-  int ext_error; /**< Non-zero if external error occurs during init or hashing */
-};
-
-/**
- * Indicates that MHD_MD5_init_one_time() function is present.
- */
-#define MHD_MD5_HAS_INIT_ONE_TIME 1
-
-/**
- * Initialise structure for MD5 calculation, allocate resources.
- *
- * This function must not be called more than one time for @a ctx.
- *
- * @param ctx the calculation context
- */
-void
-MHD_MD5_init_one_time (struct Md5CtxExt *ctx);
-
-
-/**
- * MD5 process portion of bytes.
- *
- * @param ctx the calculation context
- * @param data bytes to add to hash
- * @param length number of bytes in @a data
- */
-void
-MHD_MD5_update (struct Md5CtxExt *ctx,
-                const uint8_t *data,
-                size_t length);
-
-
-/**
- * Indicates that MHD_MD5_finish_reset() function is available
- */
-#define MHD_MD5_HAS_FINISH_RESET 1
-
-/**
- * Finalise MD5 calculation, return digest, reset hash calculation.
- *
- * @param ctx the calculation context
- * @param[out] digest set to the hash, must be #MD5_DIGEST_SIZE bytes
- */
-void
-MHD_MD5_finish_reset (struct Md5CtxExt *ctx,
-                      uint8_t digest[MD5_DIGEST_SIZE]);
-
-/**
- * Indicates that MHD_MD5_deinit() function is present
- */
-#define MHD_MD5_HAS_DEINIT 1
-
-/**
- * Free allocated resources.
- *
- * @param ctx the calculation context
- */
-void
-MHD_MD5_deinit (struct Md5CtxExt *ctx);
-
-#endif /* MHD_MD5_EXT_H */

+ 0 - 97
src/microhttpd/mhd_align.h

@@ -1,97 +0,0 @@
-/*
-  This file is part of libmicrohttpd
-  Copyright (C) 2021-2022 Karlson2k (Evgeny Grin)
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library 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, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file microhttpd/mhd_align.h
- * @brief  types alignment macros
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_ALIGN_H
-#define MHD_ALIGN_H 1
-
-#include "mhd_options.h"
-#include <stdint.h>
-#ifdef HAVE_STDDEF_H
-#include <stddef.h>
-#endif
-
-#ifdef HAVE_C_ALIGNOF
-#ifdef HAVE_STDALIGN_H
-#include <stdalign.h>
-#endif /* HAVE_STDALIGN_H */
-#define _MHD_ALIGNOF(type) alignof(type)
-#endif /* HAVE_C_ALIGNOF */
-
-#ifndef _MHD_ALIGNOF
-#if defined(_MSC_VER) && ! defined(__clang__) && _MSC_VER >= 1700
-#define _MHD_ALIGNOF(type) __alignof(type)
-#endif /* _MSC_VER >= 1700 */
-#endif /* !_MHD_ALIGNOF */
-
-#ifdef _MHD_ALIGNOF
-#if (defined(__GNUC__) && __GNUC__ < 4 && __GNUC_MINOR__ < 9 && \
-  ! defined(__clang__)) || \
-  (defined(__clang__) && __clang_major__ < 8) || \
-  (defined(__clang__) && __clang_major__ < 11 && \
-  defined(__apple_build_version__))
-/* GCC before 4.9 and clang before 8.0 have incorrect implementation of 'alignof()'
-   which returns preferred alignment instead of minimal required alignment */
-#define _MHD_ALIGNOF_UNRELIABLE 1
-#endif
-
-#if defined(_MSC_VER) && ! defined(__clang__) && _MSC_VER < 1900
-/* MSVC has the same problem as old GCC versions:
-   '__alignof()' may return "preferred" alignment instead of "required". */
-#define _MHD_ALIGNOF_UNRELIABLE 1
-#endif /* _MSC_VER < 1900 */
-#endif /* _MHD_ALIGNOF */
-
-
-#ifdef offsetof
-#define _MHD_OFFSETOF(strct, membr) offsetof(strct, membr)
-#else  /* ! offsetof */
-#define _MHD_OFFSETOF(strct, membr) (size_t)(((char*)&(((strct*)0)->membr)) - \
-                                     ((char*)((strct*)0)))
-#endif /* ! offsetof */
-
-/* Provide a limited set of alignment macros */
-/* The set could be extended as needed */
-#if defined(_MHD_ALIGNOF) && ! defined(_MHD_ALIGNOF_UNRELIABLE)
-#define _MHD_UINT32_ALIGN _MHD_ALIGNOF(uint32_t)
-#define _MHD_UINT64_ALIGN _MHD_ALIGNOF(uint64_t)
-#else  /* ! _MHD_ALIGNOF */
-struct _mhd_dummy_uint32_offset_test
-{
-  char dummy;
-  uint32_t ui32;
-};
-#define _MHD_UINT32_ALIGN \
-  _MHD_OFFSETOF(struct _mhd_dummy_uint32_offset_test, ui32)
-
-struct _mhd_dummy_uint64_offset_test
-{
-  char dummy;
-  uint64_t ui64;
-};
-#define _MHD_UINT64_ALIGN \
-  _MHD_OFFSETOF(struct _mhd_dummy_uint64_offset_test, ui64)
-#endif /* ! _MHD_ALIGNOF */
-
-#endif /* ! MHD_ALIGN_H */

+ 0 - 402
src/microhttpd/mhd_bithelpers.h

@@ -1,402 +0,0 @@
-/*
-  This file is part of libmicrohttpd
-  Copyright (C) 2019-2023 Karlson2k (Evgeny Grin)
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library 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, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file microhttpd/mhd_bithelpers.h
- * @brief  macros for bits manipulations
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_BITHELPERS_H
-#define MHD_BITHELPERS_H 1
-
-#include "mhd_options.h"
-#include <stdint.h>
-#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
-  defined(__OPTIMIZE__)))
-/* Declarations for VC & Clang/C2 built-ins */
-#include <intrin.h>
-#endif /* _MSC_FULL_VER  */
-#include "mhd_byteorder.h"
-#if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN || _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
-#include "mhd_align.h"
-#endif /* _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN ||
-          _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN */
-
-#ifndef __has_builtin
-/* Avoid precompiler errors with non-clang */
-#  define __has_builtin(x) 0
-#  define _MHD_has_builtin_dummy 1
-#endif
-
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
-
-#ifdef MHD_HAVE___BUILTIN_BSWAP32
-#define _MHD_BYTES_SWAP32(value32)  \
-  ((uint32_t) __builtin_bswap32 ((uint32_t) value32))
-#elif defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
-  defined(__OPTIMIZE__)))
-/* Clang/C2 may not inline this function if optimizations are turned off. */
-#ifndef __clang__
-#pragma intrinsic(_byteswap_ulong)
-#endif /* ! __clang__ */
-#define _MHD_BYTES_SWAP32(value32)  \
-  ((uint32_t) _byteswap_ulong ((uint32_t) value32))
-#elif \
-  __has_builtin (__builtin_bswap32)
-#define _MHD_BYTES_SWAP32(value32)  \
-  ((uint32_t) __builtin_bswap32 ((uint32_t) value32))
-#else  /* ! __has_builtin(__builtin_bswap32) */
-#define _MHD_BYTES_SWAP32(value32)                                  \
-  ( (((uint32_t) (value32)) << 24)                                  \
-    | ((((uint32_t) (value32)) & ((uint32_t) 0x0000FF00)) << 8)     \
-    | ((((uint32_t) (value32)) & ((uint32_t) 0x00FF0000)) >> 8)     \
-    | (((uint32_t) (value32))                           >> 24) )
-#endif /* ! __has_builtin(__builtin_bswap32) */
-
-#ifdef MHD_HAVE___BUILTIN_BSWAP64
-#define _MHD_BYTES_SWAP64(value64) \
-  ((uint64_t) __builtin_bswap64 ((uint64_t) value64))
-#elif defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
-  defined(__OPTIMIZE__)))
-/* Clang/C2 may not inline this function if optimizations are turned off. */
-#ifndef __clang__
-#pragma intrinsic(_byteswap_uint64)
-#endif /* ! __clang__ */
-#define _MHD_BYTES_SWAP64(value64)  \
-  ((uint64_t) _byteswap_uint64 ((uint64_t) value64))
-#elif \
-  __has_builtin (__builtin_bswap64)
-#define _MHD_BYTES_SWAP64(value64) \
-  ((uint64_t) __builtin_bswap64 ((uint64_t) value64))
-#else  /* ! __has_builtin(__builtin_bswap64) */
-#define _MHD_BYTES_SWAP64(value64)                                          \
-  ( (((uint64_t) (value64)) << 56)                                          \
-    | ((((uint64_t) (value64)) & ((uint64_t) 0x000000000000FF00)) << 40)    \
-    | ((((uint64_t) (value64)) & ((uint64_t) 0x0000000000FF0000)) << 24)    \
-    | ((((uint64_t) (value64)) & ((uint64_t) 0x00000000FF000000)) << 8)     \
-    | ((((uint64_t) (value64)) & ((uint64_t) 0x000000FF00000000)) >> 8)     \
-    | ((((uint64_t) (value64)) & ((uint64_t) 0x0000FF0000000000)) >> 24)    \
-    | ((((uint64_t) (value64)) & ((uint64_t) 0x00FF000000000000)) >> 40)    \
-    | (((uint64_t) (value64))                                   >> 56) )
-#endif /* ! __has_builtin(__builtin_bswap64) */
-
-
-/* _MHD_PUT_64BIT_LE (addr, value64)
- * put native-endian 64-bit value64 to addr
- * in little-endian mode.
- */
-/* Slow version that works with unaligned addr and with any bytes order */
-#define _MHD_PUT_64BIT_LE_SLOW(addr, value64) do {                       \
-    ((uint8_t*) (addr))[0] = (uint8_t) ((uint64_t) (value64));           \
-    ((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 8);    \
-    ((uint8_t*) (addr))[2] = (uint8_t) (((uint64_t) (value64)) >> 16);   \
-    ((uint8_t*) (addr))[3] = (uint8_t) (((uint64_t) (value64)) >> 24);   \
-    ((uint8_t*) (addr))[4] = (uint8_t) (((uint64_t) (value64)) >> 32);   \
-    ((uint8_t*) (addr))[5] = (uint8_t) (((uint64_t) (value64)) >> 40);   \
-    ((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 48);   \
-    ((uint8_t*) (addr))[7] = (uint8_t) (((uint64_t) (value64)) >> 56);   \
-} while (0)
-#if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
-#define _MHD_PUT_64BIT_LE(addr, value64)             \
-  ((*(uint64_t*) (addr)) = (uint64_t) (value64))
-#elif _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
-#define _MHD_PUT_64BIT_LE(addr, value64)             \
-  ((*(uint64_t*) (addr)) = _MHD_BYTES_SWAP64 (value64))
-#else  /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
-/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_PUT_64BIT_LE(addr, value64) do {                            \
-    ((uint8_t*) (addr))[0] = (uint8_t) ((uint64_t) (value64));           \
-    ((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 8);    \
-    ((uint8_t*) (addr))[2] = (uint8_t) (((uint64_t) (value64)) >> 16);   \
-    ((uint8_t*) (addr))[3] = (uint8_t) (((uint64_t) (value64)) >> 24);   \
-    ((uint8_t*) (addr))[4] = (uint8_t) (((uint64_t) (value64)) >> 32);   \
-    ((uint8_t*) (addr))[5] = (uint8_t) (((uint64_t) (value64)) >> 40);   \
-    ((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 48);   \
-    ((uint8_t*) (addr))[7] = (uint8_t) (((uint64_t) (value64)) >> 56);   \
-} while (0)
-/* Indicate that _MHD_PUT_64BIT_LE does not need aligned pointer */
-#define _MHD_PUT_64BIT_LE_UNALIGNED 1
-#endif /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
-
-/* Put result safely to unaligned address */
-_MHD_static_inline void
-_MHD_PUT_64BIT_LE_SAFE (void *dst, uint64_t value)
-{
-#ifndef _MHD_PUT_64BIT_LE_UNALIGNED
-  if (0 != ((uintptr_t) dst) % (_MHD_UINT64_ALIGN))
-    _MHD_PUT_64BIT_LE_SLOW (dst, value);
-  else
-#endif /* ! _MHD_PUT_64BIT_LE_UNALIGNED */
-  _MHD_PUT_64BIT_LE (dst, value);
-}
-
-
-/* _MHD_PUT_32BIT_LE (addr, value32)
- * put native-endian 32-bit value32 to addr
- * in little-endian mode.
- */
-#if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
-#define _MHD_PUT_32BIT_LE(addr,value32)             \
-  ((*(uint32_t*) (addr)) = (uint32_t) (value32))
-#elif _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
-#define _MHD_PUT_32BIT_LE(addr, value32)            \
-  ((*(uint32_t*) (addr)) = _MHD_BYTES_SWAP32 (value32))
-#else  /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
-/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_PUT_32BIT_LE(addr, value32) do {                            \
-    ((uint8_t*) (addr))[0] = (uint8_t) ((uint32_t) (value32));           \
-    ((uint8_t*) (addr))[1] = (uint8_t) (((uint32_t) (value32)) >> 8);    \
-    ((uint8_t*) (addr))[2] = (uint8_t) (((uint32_t) (value32)) >> 16);   \
-    ((uint8_t*) (addr))[3] = (uint8_t) (((uint32_t) (value32)) >> 24);   \
-} while (0)
-/* Indicate that _MHD_PUT_32BIT_LE does not need aligned pointer */
-#define _MHD_PUT_32BIT_LE_UNALIGNED 1
-#endif /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
-
-/* _MHD_GET_32BIT_LE (addr)
- * get little-endian 32-bit value storied at addr
- * and return it in native-endian mode.
- */
-#if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
-#define _MHD_GET_32BIT_LE(addr)             \
-  (*(const uint32_t*) (addr))
-#elif _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
-#define _MHD_GET_32BIT_LE(addr)             \
-  _MHD_BYTES_SWAP32 (*(const uint32_t*) (addr))
-#else  /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
-/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_GET_32BIT_LE(addr)                           \
-  ( ( (uint32_t) (((const uint8_t*) addr)[0]))            \
-    | (((uint32_t) (((const uint8_t*) addr)[1])) << 8)    \
-    | (((uint32_t) (((const uint8_t*) addr)[2])) << 16)   \
-    | (((uint32_t) (((const uint8_t*) addr)[3])) << 24) )
-/* Indicate that _MHD_GET_32BIT_LE does not need aligned pointer */
-#define _MHD_GET_32BIT_LE_UNALIGNED 1
-#endif /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
-
-
-/* _MHD_PUT_64BIT_BE (addr, value64)
- * put native-endian 64-bit value64 to addr
- * in big-endian mode.
- */
-/* Slow version that works with unaligned addr and with any bytes order */
-#define _MHD_PUT_64BIT_BE_SLOW(addr, value64) do {                       \
-    ((uint8_t*) (addr))[7] = (uint8_t) ((uint64_t) (value64));           \
-    ((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 8);    \
-    ((uint8_t*) (addr))[5] = (uint8_t) (((uint64_t) (value64)) >> 16);   \
-    ((uint8_t*) (addr))[4] = (uint8_t) (((uint64_t) (value64)) >> 24);   \
-    ((uint8_t*) (addr))[3] = (uint8_t) (((uint64_t) (value64)) >> 32);   \
-    ((uint8_t*) (addr))[2] = (uint8_t) (((uint64_t) (value64)) >> 40);   \
-    ((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 48);   \
-    ((uint8_t*) (addr))[0] = (uint8_t) (((uint64_t) (value64)) >> 56);   \
-} while (0)
-#if _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
-#define _MHD_PUT_64BIT_BE(addr, value64)             \
-  ((*(uint64_t*) (addr)) = (uint64_t) (value64))
-#elif _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
-#define _MHD_PUT_64BIT_BE(addr, value64)             \
-  ((*(uint64_t*) (addr)) = _MHD_BYTES_SWAP64 (value64))
-#else  /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
-/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_PUT_64BIT_BE(addr, value64) _MHD_PUT_64BIT_BE_SLOW(addr, value64)
-/* Indicate that _MHD_PUT_64BIT_BE does not need aligned pointer */
-#define _MHD_PUT_64BIT_BE_UNALIGNED 1
-#endif /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
-
-/* Put result safely to unaligned address */
-_MHD_static_inline void
-_MHD_PUT_64BIT_BE_SAFE (void *dst, uint64_t value)
-{
-#ifndef _MHD_PUT_64BIT_BE_UNALIGNED
-  if (0 != ((uintptr_t) dst) % (_MHD_UINT64_ALIGN))
-    _MHD_PUT_64BIT_BE_SLOW (dst, value);
-  else
-#endif /* ! _MHD_PUT_64BIT_BE_UNALIGNED */
-  _MHD_PUT_64BIT_BE (dst, value);
-}
-
-
-/* _MHD_GET_64BIT_BE (addr)
- * load 64-bit value located at addr in big endian mode.
- */
-#if _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
-#define _MHD_GET_64BIT_BE(addr)             \
-  (*(const uint64_t*) (addr))
-#elif _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
-#define _MHD_GET_64BIT_BE(addr)             \
-  _MHD_BYTES_SWAP64 (*(const uint64_t*) (addr))
-#else  /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
-/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_GET_64BIT_BE(addr)                           \
-  (   (((uint64_t) (((const uint8_t*) addr)[0])) << 56)   \
-    | (((uint64_t) (((const uint8_t*) addr)[1])) << 48)   \
-    | (((uint64_t) (((const uint8_t*) addr)[2])) << 40)   \
-    | (((uint64_t) (((const uint8_t*) addr)[3])) << 32)   \
-    | (((uint64_t) (((const uint8_t*) addr)[4])) << 24)   \
-    | (((uint64_t) (((const uint8_t*) addr)[5])) << 16)   \
-    | (((uint64_t) (((const uint8_t*) addr)[6])) << 8)    \
-    | ((uint64_t)  (((const uint8_t*) addr)[7])) )
-/* Indicate that _MHD_GET_64BIT_BE does not need aligned pointer */
-#define _MHD_GET_64BIT_BE_ALLOW_UNALIGNED 1
-#endif /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
-
-
-/* _MHD_PUT_32BIT_BE (addr, value32)
- * put native-endian 32-bit value32 to addr
- * in big-endian mode.
- */
-#if _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
-#define _MHD_PUT_32BIT_BE(addr, value32)             \
-  ((*(uint32_t*) (addr)) = (uint32_t) (value32))
-#elif _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
-#define _MHD_PUT_32BIT_BE(addr, value32)             \
-  ((*(uint32_t*) (addr)) = _MHD_BYTES_SWAP32 (value32))
-#else  /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
-/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_PUT_32BIT_BE(addr, value32) do {                            \
-    ((uint8_t*) (addr))[3] = (uint8_t) ((uint32_t) (value32));           \
-    ((uint8_t*) (addr))[2] = (uint8_t) (((uint32_t) (value32)) >> 8);    \
-    ((uint8_t*) (addr))[1] = (uint8_t) (((uint32_t) (value32)) >> 16);   \
-    ((uint8_t*) (addr))[0] = (uint8_t) (((uint32_t) (value32)) >> 24);   \
-} while (0)
-/* Indicate that _MHD_PUT_32BIT_BE does not need aligned pointer */
-#define _MHD_PUT_32BIT_BE_UNALIGNED 1
-#endif /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
-
-/* _MHD_GET_32BIT_BE (addr)
- * get big-endian 32-bit value storied at addr
- * and return it in native-endian mode.
- */
-#if _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
-#define _MHD_GET_32BIT_BE(addr)             \
-  (*(const uint32_t*) (addr))
-#elif _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
-#define _MHD_GET_32BIT_BE(addr)             \
-  _MHD_BYTES_SWAP32 (*(const uint32_t*) (addr))
-#else  /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
-/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_GET_32BIT_BE(addr)                           \
-  ( (((uint32_t) (((const uint8_t*) addr)[0])) << 24)     \
-    | (((uint32_t) (((const uint8_t*) addr)[1])) << 16)   \
-    | (((uint32_t) (((const uint8_t*) addr)[2])) << 8)    \
-    | ((uint32_t) (((const uint8_t*) addr)[3])) )
-/* Indicate that _MHD_GET_32BIT_BE does not need aligned pointer */
-#define _MHD_GET_32BIT_BE_UNALIGNED 1
-#endif /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
-
-
-/**
- * Rotate right 32-bit value by number of bits.
- * bits parameter must be more than zero and must be less than 32.
- */
-#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
-  defined(__OPTIMIZE__)))
-/* Clang/C2 do not inline this function if optimizations are turned off. */
-#ifndef __clang__
-#pragma intrinsic(_rotr)
-#endif /* ! __clang__ */
-#define _MHD_ROTR32(value32, bits) \
-  ((uint32_t) _rotr ((uint32_t) (value32),(bits)))
-#elif __has_builtin (__builtin_rotateright32)
-#define _MHD_ROTR32(value32, bits) \
-  ((uint32_t) __builtin_rotateright32 ((value32), (bits)))
-#else  /* ! __builtin_rotateright32 */
-_MHD_static_inline uint32_t
-_MHD_ROTR32 (uint32_t value32, int bits)
-{
-  bits %= 32;
-  if (0 == bits)
-    return value32;
-  /* Defined in form which modern compiler could optimize. */
-  return (value32 >> bits) | (value32 << (32 - bits));
-}
-
-
-#endif /* ! __builtin_rotateright32 */
-
-
-/**
- * Rotate left 32-bit value by number of bits.
- * bits parameter must be more than zero and must be less than 32.
- */
-#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
-  defined(__OPTIMIZE__)))
-/* Clang/C2 do not inline this function if optimizations are turned off. */
-#ifndef __clang__
-#pragma intrinsic(_rotl)
-#endif /* ! __clang__ */
-#define _MHD_ROTL32(value32, bits) \
-  ((uint32_t) _rotl ((uint32_t) (value32),(bits)))
-#elif __has_builtin (__builtin_rotateleft32)
-#define _MHD_ROTL32(value32, bits) \
-  ((uint32_t) __builtin_rotateleft32 ((value32), (bits)))
-#else  /* ! __builtin_rotateleft32 */
-_MHD_static_inline uint32_t
-_MHD_ROTL32 (uint32_t value32, int bits)
-{
-  bits %= 32;
-  if (0 == bits)
-    return value32;
-  /* Defined in form which modern compiler could optimize. */
-  return (value32 << bits) | (value32 >> (32 - bits));
-}
-
-
-#endif /* ! __builtin_rotateleft32 */
-
-
-/**
- * Rotate right 64-bit value by number of bits.
- * bits parameter must be more than zero and must be less than 64.
- */
-#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
-  defined(__OPTIMIZE__)))
-/* Clang/C2 do not inline this function if optimisations are turned off. */
-#ifndef __clang__
-#pragma intrinsic(_rotr64)
-#endif /* ! __clang__ */
-#define _MHD_ROTR64(value64, bits) \
-  ((uint64_t) _rotr64 ((uint64_t) (value64),(bits)))
-#elif __has_builtin (__builtin_rotateright64)
-#define _MHD_ROTR64(value64, bits) \
-  ((uint64_t) __builtin_rotateright64 ((value64), (bits)))
-#else  /* ! __builtin_rotateright64 */
-_MHD_static_inline uint64_t
-_MHD_ROTR64 (uint64_t value64, int bits)
-{
-  bits %= 64;
-  if (0 == bits)
-    return value64;
-  /* Defined in form which modern compiler could optimise. */
-  return (value64 >> bits) | (value64 << (64 - bits));
-}
-
-
-#endif /* ! __builtin_rotateright64 */
-
-MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
-
-#ifdef _MHD_has_builtin_dummy
-/* Remove macro function replacement to avoid misdetection in files which
- * include this header */
-#  undef __has_builtin
-#endif
-
-#endif /* ! MHD_BITHELPERS_H */

+ 0 - 169
src/microhttpd/mhd_byteorder.h

@@ -1,169 +0,0 @@
-/*
-  This file is part of libmicrohttpd
-  Copyright (C) 2015-2022 Karlson2k (Evgeny Grin)
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library 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, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file microhttpd/mhd_byteorder.h
- * @brief  macro definitions for host byte order
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_BYTEORDER_H
-#define MHD_BYTEORDER_H
-
-#include "mhd_options.h"
-
-#include <stdint.h>
-
-#ifdef HAVE_ENDIAN_H
-#include <endian.h>
-#endif
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
-#ifdef HAVE_MACHINE_ENDIAN_H
-#include <machine/endian.h>
-#endif
-
-#ifdef HAVE_SYS_ENDIAN_H
-#include <sys/endian.h>
-#endif
-
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#ifdef HAVE_SYS_BYTEORDER_H
-#include <sys/byteorder.h>
-#endif
-
-#ifdef HAVE_SYS_MACHINE_H
-#include <sys/machine.h>
-#endif
-
-#ifdef HAVE_MACHINE_PARAM_H
-#include <machine/param.h>
-#endif
-
-#ifdef HAVE_SYS_ISA_DEFS_H
-#include <sys/isa_defs.h>
-#endif
-
-#define _MHD_BIG_ENDIAN 1234
-#define _MHD_LITTLE_ENDIAN 4321
-#define _MHD_PDP_ENDIAN 2143
-
-#if defined(__BYTE_ORDER__)
-#if defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN
-#elif defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == \
-  __ORDER_LITTLE_ENDIAN__
-#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN
-#elif defined(__ORDER_PDP_ENDIAN__) && __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__
-#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN
-#endif /* __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__ */
-#elif defined(__BYTE_ORDER)
-#if defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN
-#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN
-#elif defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
-#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN
-#elif defined(__PDP_ENDIAN) && __BYTE_ORDER == __PDP_ENDIAN
-#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN
-#endif /* __BYTE_ORDER == __PDP_ENDIAN */
-#elif defined(BYTE_ORDER)
-#if defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN
-#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN
-#elif defined(LITTLE_ENDIAN) && BYTE_ORDER == LITTLE_ENDIAN
-#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN
-#elif defined(PDP_ENDIAN) && BYTE_ORDER == PDP_ENDIAN
-#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN
-#endif /* __BYTE_ORDER == _PDP_ENDIAN */
-#elif defined(_BYTE_ORDER)
-#if defined(_BIG_ENDIAN) && _BYTE_ORDER == _BIG_ENDIAN
-#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN
-#elif defined(_LITTLE_ENDIAN) && _BYTE_ORDER == _LITTLE_ENDIAN
-#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN
-#elif defined(_PDP_ENDIAN) && _BYTE_ORDER == _PDP_ENDIAN
-#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN
-#endif /* _BYTE_ORDER == _PDP_ENDIAN */
-#endif /* _BYTE_ORDER */
-
-#ifndef _MHD_BYTE_ORDER
-/* Byte order specification didn't detected in system headers */
-/* Try some guessing */
-
-#if   (defined(__BIG_ENDIAN__) && ! defined(__LITTLE_ENDIAN__)) || \
-  (defined(_BIG_ENDIAN) && ! defined(_LITTLE_ENDIAN))
-/* Seems that we are on big endian platform */
-#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN
-#elif (defined(__LITTLE_ENDIAN__) && ! defined(__BIG_ENDIAN__)) || \
-  (defined(_LITTLE_ENDIAN) && ! defined(_BIG_ENDIAN))
-/* Seems that we are on little endian platform */
-#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN
-#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || \
-  defined(__x86_64) || \
-  defined(_M_X64) || defined(_M_AMD64) || defined(i386) || defined(__i386) || \
-  defined(__i386__) || defined(__i486__) || defined(__i586__) || \
-  defined(__i686__) || \
-  defined(_M_IX86) || defined(_X86_) || defined(__THW_INTEL__)
-/* x86 family is little endian */
-#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN
-#elif defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
-  defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__)
-/* Looks like we are on ARM/MIPS in big endian mode */
-#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN
-#elif defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \
-  defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
-/* Looks like we are on ARM/MIPS in little endian mode */
-#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN
-#elif defined(__m68k__) || defined(M68000) || defined(__hppa__) || \
-  defined(__hppa) || \
-  defined(__HPPA__) || defined(__370__) || defined(__THW_370__) || \
-  defined(__s390__) || defined(__s390x__) || defined(__SYSC_ZARCH__)
-/* Looks like we are on big endian platform */
-#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN
-#elif defined(__ia64__) || defined(_IA64) || defined(__IA64__) || \
-  defined(__ia64) || \
-  defined(_M_IA64) || defined(__itanium__) || defined(__bfin__) || \
-  defined(__BFIN__) || defined(bfin) || defined(BFIN)
-/* Looks like we are on little endian platform */
-#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN
-#elif defined(_WIN32)
-/* W32 is always little endian on all platforms */
-#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN
-#elif defined(WORDS_BIGENDIAN)
-/* Use byte order detected by configure */
-#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN
-#endif /* _WIN32 */
-
-#endif /* !_MHD_BYTE_ORDER */
-
-#ifdef _MHD_BYTE_ORDER
-/* Some safety checks */
-#if defined(WORDS_BIGENDIAN) && _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN
-#error \
-  Configure detected big endian byte order but headers specify different byte order
-#elif ! defined(WORDS_BIGENDIAN) && _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
-#error \
-  Configure did not detect big endian byte order but headers specify big endian byte order
-#endif /* !WORDS_BIGENDIAN && _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN */
-#endif /* _MHD_BYTE_ORDER */
-
-#endif /* !MHD_BYTEORDER_H */

+ 0 - 98
src/microhttpd/mhd_md5_wrap.h

@@ -1,98 +0,0 @@
-/*
-     This file is part of GNU libmicrohttpd
-     Copyright (C) 2022 Evgeny Grin (Karlson2k)
-
-     GNU libmicrohttpd is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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 GNU libmicrohttpd.
-     If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file microhttpd/mhd_md5_wrap.h
- * @brief  Simple wrapper for selection of built-in/external MD5 implementation
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_MD5_WRAP_H
-#define MHD_MD5_WRAP_H 1
-
-#include "mhd_options.h"
-#ifndef MHD_MD5_SUPPORT
-#error This file must be used only when MD5 is enabled
-#endif
-#ifndef MHD_MD5_TLSLIB
-#include "md5.h"
-#else  /* MHD_MD5_TLSLIB */
-#include "md5_ext.h"
-#endif /* MHD_MD5_TLSLIB */
-
-#ifndef MD5_DIGEST_SIZE
-/**
- * Size of MD5 resulting digest in bytes
- * This is the final digest size, not intermediate hash.
- */
-#define MD5_DIGEST_SIZE (16)
-#endif /* ! MD5_DIGEST_SIZE */
-
-#ifndef MD5_DIGEST_STRING_SIZE
-/**
- * Size of MD5 digest string in chars including termination NUL.
- */
-#define MD5_DIGEST_STRING_SIZE ((MD5_DIGEST_SIZE) * 2 + 1)
-#endif /* ! MD5_DIGEST_STRING_SIZE */
-
-#ifndef MHD_MD5_TLSLIB
-/**
- * Universal ctx type mapped for chosen implementation
- */
-#define Md5CtxWr Md5Ctx
-#else  /* MHD_MD5_TLSLIB */
-/**
- * Universal ctx type mapped for chosen implementation
- */
-#define Md5CtxWr Md5CtxExt
-#endif /* MHD_MD5_TLSLIB */
-
-#ifndef MHD_MD5_HAS_INIT_ONE_TIME
-/**
- * Setup and prepare ctx for hash calculation
- */
-#define MHD_MD5_init_one_time(ctx) MHD_MD5_init(ctx)
-#endif /* ! MHD_MD5_HAS_INIT_ONE_TIME */
-
-#ifndef MHD_MD5_HAS_FINISH_RESET
-/**
- * Re-use the same ctx for the new hashing after digest calculated
- */
-#define MHD_MD5_reset(ctx) MHD_MD5_init(ctx)
-/**
- * Finalise MD5 calculation, return digest, reset hash calculation.
- */
-#define MHD_MD5_finish_reset(ctx,digest) MHD_MD5_finish(ctx,digest), \
-                                         MHD_MD5_reset(ctx)
-
-#else  /* MHD_MD5_HAS_FINISH_RESET */
-#define MHD_MD5_reset(ctx) (void)0
-#endif /* MHD_MD5_HAS_FINISH_RESET */
-
-#ifndef MHD_MD5_HAS_DEINIT
-#define MHD_MD5_deinit(ignore) (void)0
-#endif /* HAVE_MD5_DEINIT */
-
-/* Sanity checks */
-
-#if ! defined(MHD_MD5_HAS_FINISH_RESET) && ! defined(MHD_MD5_HAS_FINISH)
-#error Required at least one of MHD_MD5_finish_reset(), MHD_MD5_finish_reset()
-#endif /* ! MHD_MD5_HAS_FINISH_RESET && ! MHD_MD5_HAS_FINISH */
-
-#endif /* MHD_MD5_WRAP_H */

+ 0 - 100
src/microhttpd/mhd_sha256_wrap.h

@@ -1,100 +0,0 @@
-/*
-     This file is part of GNU libmicrohttpd
-     Copyright (C) 2022 Evgeny Grin (Karlson2k)
-
-     GNU libmicrohttpd is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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 GNU libmicrohttpd.
-     If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file microhttpd/mhd_sha256_wrap.h
- * @brief  Simple wrapper for selection of built-in/external SHA-256
- *         implementation
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_SHA256_WRAP_H
-#define MHD_SHA256_WRAP_H 1
-
-#include "mhd_options.h"
-#include "mhd_options.h"
-#ifndef MHD_SHA256_SUPPORT
-#error This file must be used only when SHA-256 is enabled
-#endif
-#ifndef MHD_SHA256_TLSLIB
-#include "sha256.h"
-#else  /* MHD_SHA256_TLSLIB */
-#include "sha256_ext.h"
-#endif /* MHD_SHA256_TLSLIB */
-
-#ifndef SHA256_DIGEST_SIZE
-/**
- * Size of SHA-256 resulting digest in bytes
- * This is the final digest size, not intermediate hash.
- */
-#define SHA256_DIGEST_SIZE (32)
-#endif /* ! SHA256_DIGEST_SIZE */
-
-#ifndef SHA256_DIGEST_STRING_SIZE
-/**
- * Size of MD5 digest string in chars including termination NUL.
- */
-#define SHA256_DIGEST_STRING_SIZE ((SHA256_DIGEST_SIZE) * 2 + 1)
-#endif /* ! SHA256_DIGEST_STRING_SIZE */
-
-#ifndef MHD_SHA256_TLSLIB
-/**
- * Universal ctx type mapped for chosen implementation
- */
-#define Sha256CtxWr Sha256Ctx
-#else  /* MHD_SHA256_TLSLIB */
-/**
- * Universal ctx type mapped for chosen implementation
- */
-#define Sha256CtxWr Sha256CtxExt
-#endif /* MHD_SHA256_TLSLIB */
-
-#ifndef MHD_SHA256_HAS_INIT_ONE_TIME
-/**
- * Setup and prepare ctx for hash calculation
- */
-#define MHD_SHA256_init_one_time(ctx) MHD_SHA256_init(ctx)
-#endif /* ! MHD_SHA256_HAS_INIT_ONE_TIME */
-
-#ifndef MHD_SHA256_HAS_FINISH_RESET
-/**
- * Re-use the same ctx for the new hashing after digest calculated
- */
-#define MHD_SHA256_reset(ctx) MHD_SHA256_init(ctx)
-/**
- * Finalise MD5 calculation, return digest, reset hash calculation.
- */
-#define MHD_SHA256_finish_reset(ctx,digest) MHD_SHA256_finish(ctx,digest), \
-                                            MHD_SHA256_reset(ctx)
-
-#else  /* MHD_SHA256_HAS_FINISH_RESET */
-#define MHD_SHA256_reset(ctx) (void)0
-#endif /* MHD_SHA256_HAS_FINISH_RESET */
-
-#ifndef MHD_SHA256_HAS_DEINIT
-#define MHD_SHA256_deinit(ignore) (void)0
-#endif /* HAVE_SHA256_DEINIT */
-
-/* Sanity checks */
-
-#if ! defined(MHD_SHA256_HAS_FINISH_RESET) && ! defined(MHD_SHA256_HAS_FINISH)
-#error Required MHD_SHA256_finish_reset() or MHD_SHA256_finish_reset()
-#endif /* ! MHD_SHA256_HAS_FINISH_RESET && ! MHD_SHA256_HAS_FINISH */
-
-#endif /* MHD_SHA256_WRAP_H */

+ 0 - 122
src/microhttpd/sha256.h

@@ -1,122 +0,0 @@
-/*
-     This file is part of libmicrohttpd
-     Copyright (C) 2019-2022 Evgeny Grin (Karlson2k)
-
-     This library is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file microhttpd/sha256.h
- * @brief  Calculation of SHA-256 digest
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_SHA256_H
-#define MHD_SHA256_H 1
-
-#include "mhd_options.h"
-#include <stdint.h>
-#ifdef HAVE_STDDEF_H
-#include <stddef.h>  /* for size_t */
-#endif /* HAVE_STDDEF_H */
-
-
-/**
- *  Digest is kept internally as 8 32-bit words.
- */
-#define SHA256_DIGEST_SIZE_WORDS 8
-
-/**
- * Number of bits in single SHA-256 word
- */
-#define SHA256_WORD_SIZE_BITS 32
-
-/**
- * Number of bytes in single SHA-256 word
- * used to process data
- */
-#define SHA256_BYTES_IN_WORD (SHA256_WORD_SIZE_BITS / 8)
-
-/**
- * Size of SHA-256 digest in bytes
- */
-#define SHA256_DIGEST_SIZE (SHA256_DIGEST_SIZE_WORDS * SHA256_BYTES_IN_WORD)
-
-/**
- * Size of SHA-256 digest string in chars including termination NUL
- */
-#define SHA256_DIGEST_STRING_SIZE ((SHA256_DIGEST_SIZE) * 2 + 1)
-
-/**
- * Size of single processing block in bits
- */
-#define SHA256_BLOCK_SIZE_BITS 512
-
-/**
- * Size of single processing block in bytes
- */
-#define SHA256_BLOCK_SIZE (SHA256_BLOCK_SIZE_BITS / 8)
-
-/**
- * Size of single processing block in bytes
- */
-#define SHA256_BLOCK_SIZE_WORDS (SHA256_BLOCK_SIZE_BITS / SHA256_WORD_SIZE_BITS)
-
-
-struct Sha256Ctx
-{
-  uint32_t H[SHA256_DIGEST_SIZE_WORDS];     /**< Intermediate hash value / digest at end of calculation */
-  uint32_t buffer[SHA256_BLOCK_SIZE_WORDS]; /**< SHA256 input data buffer */
-  uint64_t count;                           /**< number of bytes, mod 2^64 */
-};
-
-/**
- * Initialise structure for SHA256 calculation.
- *
- * @param ctx must be a `struct Sha256Ctx *`
- */
-void
-MHD_SHA256_init (struct Sha256Ctx *ctx);
-
-
-/**
- * Process portion of bytes.
- *
- * @param ctx must be a `struct Sha256Ctx *`
- * @param data bytes to add to hash
- * @param length number of bytes in @a data
- */
-void
-MHD_SHA256_update (struct Sha256Ctx *ctx,
-                   const uint8_t *data,
-                   size_t length);
-
-
-/**
- * Finalise SHA256 calculation, return digest.
- *
- * @param ctx must be a `struct Sha256Ctx *`
- * @param[out] digest set to the hash, must be #SHA256_DIGEST_SIZE bytes
- */
-void
-MHD_SHA256_finish (struct Sha256Ctx *ctx,
-                   uint8_t digest[SHA256_DIGEST_SIZE]);
-
-/**
- * Indicates that function MHD_SHA256_finish() (without context reset) is available
- */
-#define MHD_SHA256_HAS_FINISH 1
-
-#endif /* MHD_SHA256_H */

+ 0 - 137
src/microhttpd/sha512_256.h

@@ -1,137 +0,0 @@
-/*
-     This file is part of GNU libmicrohttpd
-     Copyright (C) 2022 Evgeny Grin (Karlson2k)
-
-     GNU libmicrohttpd is free software; you can redistribute it and/or
-     modify it under the terms of the GNU Lesser General Public
-     License as published by the Free Software Foundation; either
-     version 2.1 of the License, or (at your option) any later version.
-
-     This library 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, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file microhttpd/sha512_256.h
- * @brief  Calculation of SHA-512/256 digest
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_SHA512_256_H
-#define MHD_SHA512_256_H 1
-
-#include "mhd_options.h"
-#include <stdint.h>
-#ifdef HAVE_STDDEF_H
-#include <stddef.h>  /* for size_t */
-#endif /* HAVE_STDDEF_H */
-
-
-/**
- * Number of bits in single SHA-512/256 word.
- */
-#define SHA512_256_WORD_SIZE_BITS 64
-
-/**
- * Number of bytes in single SHA-512/256 word.
- */
-#define SHA512_256_BYTES_IN_WORD (SHA512_256_WORD_SIZE_BITS / 8)
-
-/**
- * Hash is kept internally as 8 64-bit words.
- * This is intermediate hash size, used during computing the final digest.
- */
-#define SHA512_256_HASH_SIZE_WORDS 8
-
-/**
- * Size of SHA-512/256 resulting digest in bytes.
- * This is the final digest size, not intermediate hash.
- */
-#define SHA512_256_DIGEST_SIZE_WORDS (SHA512_256_HASH_SIZE_WORDS  / 2)
-
-/**
- * Size of SHA-512/256 resulting digest in bytes
- * This is the final digest size, not intermediate hash.
- */
-#define SHA512_256_DIGEST_SIZE \
-  (SHA512_256_DIGEST_SIZE_WORDS * SHA512_256_BYTES_IN_WORD)
-
-/**
- * Size of SHA-512/256 digest string in chars including termination NUL.
- */
-#define SHA512_256_DIGEST_STRING_SIZE ((SHA512_256_DIGEST_SIZE) * 2 + 1)
-
-/**
- * Size of SHA-512/256 single processing block in bits.
- */
-#define SHA512_256_BLOCK_SIZE_BITS 1024
-
-/**
- * Size of SHA-512/256 single processing block in bytes.
- */
-#define SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8)
-
-/**
- * Size of SHA-512/256 single processing block in words.
- */
-#define SHA512_256_BLOCK_SIZE_WORDS \
- (SHA512_256_BLOCK_SIZE_BITS / SHA512_256_WORD_SIZE_BITS)
-
-
-/**
- * SHA-512/256 calculation context
- */
-struct Sha512_256Ctx
-{
-  uint64_t H[SHA512_256_HASH_SIZE_WORDS];       /**< Intermediate hash value  */
-  uint64_t buffer[SHA512_256_BLOCK_SIZE_WORDS]; /**< SHA512_256 input data buffer */
-  /**
-   * The number of bytes, lower part
-   */
-  uint64_t count;
-  /**
-   * The number of bits, high part.
-   * Unlike lower part, this counts the number of bits, not bytes.
-   */
-  uint64_t count_bits_hi;
-};
-
-/**
- * Initialise structure for SHA-512/256 calculation.
- *
- * @param ctx the calculation context
- */
-void
-MHD_SHA512_256_init (struct Sha512_256Ctx *ctx);
-
-
-/**
- * Process portion of bytes.
- *
- * @param ctx the calculation context
- * @param data bytes to add to hash
- * @param length number of bytes in @a data
- */
-void
-MHD_SHA512_256_update (struct Sha512_256Ctx *ctx,
-                       const uint8_t *data,
-                       size_t length);
-
-
-/**
- * Finalise SHA-512/256 calculation, return digest.
- *
- * @param ctx the calculation context
- * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
- */
-void
-MHD_SHA512_256_finish (struct Sha512_256Ctx *ctx,
-                       uint8_t digest[SHA512_256_DIGEST_SIZE]);
-
-#endif /* MHD_SHA512_256_H */