gnunet-svn
[Top][All Lists]
Advanced

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

[libmicrohttpd] 21/22: Implemented support for hash calculation by GnuTL


From: gnunet
Subject: [libmicrohttpd] 21/22: Implemented support for hash calculation by GnuTLS lib functions
Date: Sun, 25 Sep 2022 17:43:56 +0200

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

karlson2k pushed a commit to branch master
in repository libmicrohttpd.

commit 172b0eeb45e771ca0df56697bcfb581cc8a96a88
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
AuthorDate: Sat Sep 24 20:13:16 2022 +0300

    Implemented support for hash calculation by GnuTLS lib functions
---
 configure.ac                                  | 256 ++++++++--
 src/include/microhttpd.h                      |  39 +-
 src/microhttpd/Makefile.am                    |  46 +-
 src/microhttpd/daemon.c                       |   6 +
 src/microhttpd/digestauth.c                   | 659 ++++++++++++++++++++------
 src/microhttpd/md5.h                          |   5 +
 src/microhttpd/md5_ext.c                      |  95 ++++
 src/microhttpd/md5_ext.h                      | 115 +++++
 src/microhttpd/mhd_md5_wrap.h                 |  98 ++++
 src/microhttpd/mhd_sha256_wrap.h              | 100 ++++
 src/microhttpd/sha256.h                       |   5 +
 src/microhttpd/sha256_ext.c                   |  95 ++++
 src/microhttpd/sha256_ext.h                   | 115 +++++
 src/microhttpd/test_dauth_userdigest.c        |  14 +
 src/microhttpd/test_dauth_userhash.c          |  14 +
 src/microhttpd/test_md5.c                     |  95 +++-
 src/microhttpd/test_sha256.c                  |  90 +++-
 src/testcurl/test_digestauth.c                |  19 +-
 src/testcurl/test_digestauth2.c               |  14 +-
 src/testcurl/test_digestauth_concurrent.c     |  18 +-
 src/testcurl/test_digestauth_sha256.c         |  19 +-
 src/testcurl/test_digestauth_with_arguments.c |  11 +-
 w32/common/MHD_config.h                       |   6 +
 w32/common/libmicrohttpd-files.vcxproj        |   2 +
 w32/common/libmicrohttpd-filters.vcxproj      |   6 +
 25 files changed, 1677 insertions(+), 265 deletions(-)

diff --git a/configure.ac b/configure.ac
index 31ae78ec..1b6310da 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3048,8 +3048,8 @@ AC_MSG_RESULT([[$enable_bauth]])
 AC_MSG_CHECKING([[whether to support HTTP Digest authentication]])
 AC_ARG_ENABLE([dauth],
                [AS_HELP_STRING([--disable-dauth], [disable HTTP Digest 
Authentication support])],
-               [enable_dauth=${enableval}],
-               [enable_dauth=yes])
+               [enable_dauth="${enableval}"],
+               [enable_dauth="yes"])
 AS_IF([[test "x$enable_dauth" != "xno"]],
   [ enable_dauth=yes
     AC_DEFINE([DAUTH_SUPPORT],[1],[Define to 1 if libmicrohttpd is compiled 
with Digest Auth support.]) ])
@@ -3084,57 +3084,249 @@ AC_MSG_RESULT([[$enable_cookie]])
 
 # optional: MD5 support for Digest Auth. Enabled by default.
 AC_ARG_ENABLE([[md5]],
-  [AS_HELP_STRING([[--disable-md5]],[disable MD5 hashing support for Digest 
Authentication])
+  [AS_HELP_STRING([[--enable-md5=TYPE]],[enable TYPE of MD5 hashing code (yes, 
no, builtin, tlslib) [yes if dauth enabled]])
   ],
   [
-    AS_VAR_IF([[enable_md5]],[["yes"]],
+    AS_VAR_IF([enable_md5],["internal"],[enable_md5='builtin'])
+    AS_VAR_IF([enable_md5],["built-in"],[enable_md5='builtin'])
+    AS_VAR_IF([enable_dauth],["yes"],[],
       [
-        AS_VAR_IF([enable_dauth],["yes"],[],
+        AS_VAR_IF([enable_md5],["no"],[],
           [
-            AC_MSG_WARN([The parameter --enable-md5 is ignored as Digest 
Authentication is disabled])
+            AC_MSG_WARN([The parameter --enable-md5=${enable_md5} is ignored 
as Digest Authentication is disabled])
             enable_md5='no'
           ]
         )
-      ],[[enable_md5='no']]
+      ]
     )
   ], [[enable_md5="${enable_dauth}"]]
 )
+AS_CASE([${enable_md5}],[yes|tlslib],
+  [
+    AS_IF([test "x${enable_compact_code}" != "xno" || test "x$enable_md5" = 
"xtlslib"],
+      [
+        AS_IF([test "x$enable_https" = "xyes"],
+          [
+            AC_CACHE_CHECK([whether GnuTLS supports MD5 
hashing],[mhd_cv_gnutls_md5],
+              [
+                CFLAGS="${CFLAGS_ac} ${GNUTLS_CFLAGS} ${user_CFLAGS}"
+                CPPFLAGS="${CPPFLAGS_ac} ${MHD_TLS_LIB_CPPFLAGS} 
${user_CPPFLAGS}"
+                CFLAGS="${CFLAGS_ac} ${MHD_TLS_LIB_CFLAGS} ${user_CFLAGS}"
+                LDFLAGS="${LDFLAGS_ac} ${MHD_TLS_LIB_LDFLAGS} ${user_LDFLAGS}"
+                save_LIBS="$LIBS"
+                LIBS="${MHD_TLS_LIBDEPS} ${LIBS}"
+                AC_LINK_IFELSE(
+                  [
+                    AC_LANG_PROGRAM(
+                      [[
+#include <gnutls/crypto.h>
+                      ]],
+                      [[
+    gnutls_hash_hd_t hash_handle;
+    unsigned char digest[16];
+    int exit_code;
+
+    if (0 == gnutls_hash_init(&hash_handle, GNUTLS_DIG_MD5))
+    {
+      if (0 == gnutls_hash(hash_handle, "", 1))
+      {
+        gnutls_hash_output(hash_handle, digest);
+        if (0x93 == digest[0])
+          exit_code = 0;
+        else
+          exit_code = 7;
+      }
+      else
+        exit_code = 5;
+      gnutls_hash_deinit(hash_handle, (void *)0);
+    }
+    else
+      exit_code = 2;
+    if (exit_code)
+      return exit_code;
+                      ]]
+                    )
+                  ],
+                  [mhd_cv_gnutls_md5='yes'],[mhd_cv_gnutls_md5='no']
+                )
+                LIBS="${save_LIBS}"
+                CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
+                CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}"
+                CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
+                LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}"
+              ]
+            )
+            AS_VAR_IF([mhd_cv_gnutls_md5],["no"],
+              [
+                AS_VAR_IF([enable_md5],["tlslib"],
+                  [AC_MSG_FAILURE([TLS library MD5 implementation is not 
available])]
+                )
+                enable_md5="builtin"
+              ],
+              [enable_md5="tlslib"]
+            )
+          ],
+          [
+            AS_VAR_IF([enable_md5],["tlslib"],
+              [AC_MSG_ERROR([HTTPS is not enabled, TLS library MD5 
implementation cannot be used])]
+            )
+            enable_md5="builtin"
+          ]
+        )
+      ],
+      [
+        enable_md5="builtin"
+      ]
+    )
+  ]
+)
 AC_MSG_CHECKING([[whether to support MD5]])
-AS_VAR_IF([[enable_md5]],[["yes"]],
+AS_UNSET([enable_md5_MSG])
+AS_CASE([${enable_md5}],
+  [builtin],[enable_md5_MSG='yes, built-in'],
+  [tlslib],[enable_md5_MSG='yes, external (TLS library)'],
+  [no],[enable_md5_MSG='no'],
+  [yes],[AC_MSG_ERROR([configure internal error: unexpected variable value])],
+  [AC_MSG_ERROR([Unrecognized parameter --enable-md5=${enable_md5}])]
+)
+AS_IF([test "x${enable_md5}" = "xbuiltin" || test "x${enable_md5}" = "xtlslib" 
],
   [
-   AC_DEFINE([[MHD_MD5_SUPPORT]],[[1]],
-     [Define to 1 if libmicrohttpd is compiled with MD5 hashing support.])
+    AC_DEFINE([[MHD_MD5_SUPPORT]],[[1]],
+      [Define to 1 if libmicrohttpd is compiled with MD5 hashing support.])
   ]
 )
-AM_CONDITIONAL([ENABLE_MD5], [[test "x${enable_md5}" = "xyes"]])
-AC_MSG_RESULT([[${enable_md5}]])
+AS_IF([test "x${enable_md5}" = "xtlslib" ],
+  [
+    AC_DEFINE([[MHD_MD5_TLSLIB]],[[1]],
+      [Define to 1 if libmicrohttpd is compiled with MD5 hashing by TLS 
library.])
+  ]
+)
+AM_CONDITIONAL([ENABLE_MD5], [[test "x${enable_md5}" = "xbuiltin" || test 
"x${enable_md5}" = "xtlslib" ]])
+AM_CONDITIONAL([ENABLE_MD5_EXT], [[test "x${enable_md5}" = "xtlslib" ]])
+AC_MSG_RESULT([[${enable_md5_MSG}]])
 
 # optional: SHA-256 support for Digest Auth. Enabled by default.
 AC_ARG_ENABLE([[sha256]],
-  [AS_HELP_STRING([[--disable-sha256]],[disable SHA-256 hashing support for 
Digest Authentication])
+  [AS_HELP_STRING([[--enable-sha256=TYPE]],[enable TYPE of SHA-256 hashing 
code (yes, no, builtin, tlslib) [yes if dauth enabled]])
   ],
   [
-    AS_VAR_IF([[enable_sha256]],[["yes"]],
+    AS_VAR_IF([enable_sha256],["internal"],[enable_sha256='builtin'])
+    AS_VAR_IF([enable_sha256],["built-in"],[enable_sha256='builtin'])
+    AS_VAR_IF([enable_dauth],["yes"],[],
       [
-        AS_VAR_IF([enable_dauth],["yes"],[],
+        AS_VAR_IF([enable_sha256],["no"],[],
           [
-            AC_MSG_WARN([The parameter --enable-sha256 is ignored as Digest 
Authentication is disabled])
+            AC_MSG_WARN([The parameter --enable-sha256=${enable_sha256} is 
ignored as Digest Authentication is disabled])
             enable_sha256='no'
           ]
         )
-      ],[[enable_sha256='no']]
+      ]
     )
   ], [[enable_sha256="${enable_dauth}"]]
 )
+AS_CASE([${enable_sha256}],[yes|tlslib],
+  [
+    AS_IF([test "x${enable_compact_code}" != "xno" || test "x$enable_sha256" = 
"xtlslib"],
+      [
+        AS_IF([test "x$enable_https" = "xyes"],
+          [
+            AC_CACHE_CHECK([whether GnuTLS supports sha256 
hashing],[mhd_cv_gnutls_sha256],
+              [
+                CFLAGS="${CFLAGS_ac} ${GNUTLS_CFLAGS} ${user_CFLAGS}"
+                CPPFLAGS="${CPPFLAGS_ac} ${MHD_TLS_LIB_CPPFLAGS} 
${user_CPPFLAGS}"
+                CFLAGS="${CFLAGS_ac} ${MHD_TLS_LIB_CFLAGS} ${user_CFLAGS}"
+                LDFLAGS="${LDFLAGS_ac} ${MHD_TLS_LIB_LDFLAGS} ${user_LDFLAGS}"
+                save_LIBS="$LIBS"
+                LIBS="${MHD_TLS_LIBDEPS} ${LIBS}"
+                AC_LINK_IFELSE(
+                  [
+                    AC_LANG_PROGRAM(
+                      [[
+#include <gnutls/crypto.h>
+                      ]],
+                      [[
+    gnutls_hash_hd_t hash_handle;
+    unsigned char digest[32];
+    int exit_code;
+
+    if (0 == gnutls_hash_init(&hash_handle, GNUTLS_DIG_SHA256))
+    {
+      if (0 == gnutls_hash(hash_handle, "", 1))
+      {
+        gnutls_hash_output(hash_handle, digest);
+        if (0x6e == digest[0])
+          exit_code = 0;
+        else
+          exit_code = 7;
+      }
+      else
+        exit_code = 5;
+      gnutls_hash_deinit(hash_handle, (void *)0);
+    }
+    else
+      exit_code = 2;
+    if (exit_code)
+      return exit_code;
+                      ]]
+                    )
+                  ],
+                  [mhd_cv_gnutls_sha256='yes'],[mhd_cv_gnutls_sha256='no']
+                )
+                LIBS="${save_LIBS}"
+                CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
+                CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}"
+                CFLAGS="${CFLAGS_ac} ${user_CFLAGS}"
+                LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}"
+              ]
+            )
+            AS_VAR_IF([mhd_cv_gnutls_sha256],["no"],
+              [
+                AS_VAR_IF([enable_sha256],["tlslib"],
+                  [AC_MSG_FAILURE([TLS library SHA-256 implementation is not 
available])]
+                )
+                enable_sha256="builtin"
+              ],
+              [enable_sha256="tlslib"]
+            )
+          ],
+          [
+            AS_VAR_IF([enable_sha256],["tlslib"],
+              [AC_MSG_ERROR([HTTPS is not enabled, TLS library SHA-256 
implementation cannot be used])]
+            )
+            enable_sha256="builtin"
+          ]
+        )
+      ],
+      [
+        enable_sha256="builtin"
+      ]
+    )
+  ]
+)
 AC_MSG_CHECKING([[whether to support SHA-256]])
-AS_VAR_IF([[enable_sha256]],[["yes"]],
+AS_UNSET([enable_sha256_MSG])
+AS_CASE([${enable_sha256}],
+  [builtin],[enable_sha256_MSG='yes, built-in'],
+  [tlslib],[enable_sha256_MSG='yes, external (TLS library)'],
+  [no],[enable_sha256_MSG='no'],
+  [yes],[AC_MSG_ERROR([configure internal error: unexpected variable value])],
+  [AC_MSG_ERROR([Unrecognized parameter --enable-sha256=${enable_sha256}])]
+)
+AS_IF([test "x${enable_sha256}" = "xbuiltin" || test "x${enable_sha256}" = 
"xtlslib" ],
   [
-   AC_DEFINE([[MHD_SHA256_SUPPORT]],[[1]],
-     [Define to 1 if libmicrohttpd is compiled with SHA-256 hashing support.])
+    AC_DEFINE([[MHD_SHA256_SUPPORT]],[[1]],
+      [Define to 1 if libmicrohttpd is compiled with SHA-256 hashing support.])
   ]
 )
-AM_CONDITIONAL([ENABLE_SHA256], [[test "x${enable_sha256}" = "xyes"]])
-AC_MSG_RESULT([[${enable_sha256}]])
+AS_IF([test "x${enable_sha256}" = "xtlslib" ],
+  [
+    AC_DEFINE([[MHD_SHA256_TLSLIB]],[[1]],
+      [Define to 1 if libmicrohttpd is compiled with SHA-256 hashing by TLS 
library.])
+  ]
+)
+AM_CONDITIONAL([ENABLE_SHA256], [[test "x${enable_sha256}" = "xbuiltin" || 
test "x${enable_sha256}" = "xtlslib" ]])
+AM_CONDITIONAL([ENABLE_SHA256_EXT], [[test "x${enable_sha256}" = "xtlslib" ]])
+AC_MSG_RESULT([[${enable_sha256_MSG}]])
 
 # optional: SHA-512/256 support for Digest Auth. Enabled by default.
 AC_ARG_ENABLE([[sha512-256]],
@@ -3154,6 +3346,12 @@ AC_ARG_ENABLE([[sha512-256]],
   ], [[enable_sha512_256="${enable_dauth}"]]
 )
 AC_MSG_CHECKING([[whether to support SHA-512/256]])
+AS_UNSET([enable_sha512_256_MSG])
+AS_CASE([${enable_sha512_256}],
+  [yes],[enable_sha512_256_MSG='yes, built-in'],
+  [no],[enable_sha512_256_MSG='no'],
+  [AC_MSG_ERROR([Unrecognized parameter 
--enable-sha512-256=${enable_sha512_256}])]
+)
 AS_VAR_IF([[enable_sha512_256]],[["yes"]],
   [
    AC_DEFINE([[MHD_SHA512_256_SUPPORT]],[[1]],
@@ -3161,11 +3359,11 @@ AS_VAR_IF([[enable_sha512_256]],[["yes"]],
   ]
 )
 AM_CONDITIONAL([ENABLE_SHA512_256], [[test "x${enable_sha512_256}" = "xyes"]])
-AC_MSG_RESULT([[${enable_sha512_256}]])
+AC_MSG_RESULT([[${enable_sha512_256_MSG}]])
 
 AS_IF([test "x$enable_dauth" != "xno"],
   [
-    AS_IF([test "x${enable_md5}" != "xyes" &&  test "x${enable_sha256}" != 
"xyes" && test "x${enable_sha512_256}" != "xyes"],
+    AS_IF([test "x${enable_md5}" = "xno" &&  test "x${enable_sha256}" = "xno" 
&& test "x${enable_sha512_256}" != "xyes"],
       [AC_MSG_ERROR([At least one hashing algorithm must be enabled if Digest 
Auth is enabled])]
     )
   ]
@@ -4052,9 +4250,9 @@ AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} 
Configuration Summary:
   Messages:          ${enable_messages}
   Basic auth.:       ${enable_bauth}
   Digest auth.:      ${enable_dauth}
-  MD5:               ${enable_md5}
-  SHA-256:           ${enable_sha256}
-  SHA-512/256:       ${enable_sha512_256}
+  MD5:               ${enable_md5_MSG}
+  SHA-256:           ${enable_sha256_MSG}
+  SHA-512/256:       ${enable_sha512_256_MSG}
   HTTP "Upgrade":    ${enable_httpupgrade}
   Cookie parsing:    ${enable_cookie}
   Postproc:          ${enable_postprocessor}
@@ -4075,8 +4273,8 @@ AS_IF([test "x$enable_https" = "xyes"],
 
 AS_IF([test "x$enable_bauth" != "xyes" || \
    test "x$enable_dauth" != "xyes" || \
-   test "x${enable_md5}" != "xyes" || \
-   test "x${enable_sha256}" != "xyes" || \
+   test "x${enable_md5}" = "xno" || \
+   test "x${enable_sha256}" = "xno" || \
    test "x${enable_sha512_256}" != "xyes" || \
    test "x$enable_httpupgrade" != "xyes" || \
    test "x$enable_cookie" != "xyes" || \
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index e5f0abac..5e744b69 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -96,7 +96,7 @@ extern "C"
  * they are parsed as decimal numbers.
  * Example: 0x01093001 = 1.9.30-1.
  */
-#define MHD_VERSION 0x00097539
+#define MHD_VERSION 0x00097540
 
 /* If generic headers don't work on your platform, include headers
    which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t',
@@ -4691,8 +4691,10 @@ enum MHD_DigestAuthMultiAlgo3
  *                          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.
+ * @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)
  * @note Available since #MHD_VERSION 0x00097535
  * @ingroup authentication
  */
@@ -4736,8 +4738,10 @@ MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 
algo3,
  *                          userhash string
  * @param bin_buf_size the size of the @a userhash_bin buffer, must be
  *                     at least #MHD_digest_get_hash_size(algo3)*2+1 chars long
- * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or
- *                             if @a algo3 algorithm is not supported.
+ * @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).
  * @note Available since #MHD_VERSION 0x00097535
  * @ingroup authentication
  */
@@ -5287,8 +5291,10 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
  *                            userdigest upon return
  * @param userdigest_bin the size of the @a userdigest_bin buffer, must be
  *                       at least #MHD_digest_get_hash_size(algo3) bytes long
- * @return MHD_YES on success, MHD_NO if @a userdigest_bin is too small or
- *                             if @a algo3 algorithm is not supported.
+ * @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 0x00097535
  * @ingroup authentication
@@ -5651,6 +5657,9 @@ MHD_queue_auth_fail_response (struct MHD_Connection 
*connection,
                               int signal_stale);
 
 
+/* ********************* Basic Authentication functions *************** */
+
+
 /**
  * Information decoded from Basic Authentication client's header.
  *
@@ -6138,7 +6147,21 @@ enum MHD_FEATURE
    * module is built.
    * @note Available since #MHD_VERSION 0x00097536
    */
-  MHD_FEATURE_DIGEST_AUTH_USERHASH = 31
+  MHD_FEATURE_DIGEST_AUTH_USERHASH = 31,
+
+  /**
+   * Get whether any of hashing algorithms is implemented by external
+   * function (like TLS library) and may fail due to external conditions,
+   * like "out-of-memory".
+   *
+   * If result is #MHD_YES then functions which use hash calculations
+   * like #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_check3() and 
others
+   * potentially may fail even with valid input because of out-of-memory error
+   * or crypto accelerator device failure, however in practice such fails are
+   * unlikely.
+   * @note Available since #MHD_VERSION 0x00097540
+   */
+  MHD_FEATURE_EXTERN_HASH = 32
 };
 
 
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index 10a32f59..c4c15608 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -170,12 +170,26 @@ libmicrohttpd_la_SOURCES += \
   digestauth.c digestauth.h \
   mhd_bithelpers.h mhd_byteorder.h mhd_align.h
 if ENABLE_MD5
+libmicrohttpd_la_SOURCES += \
+  mhd_md5_wrap.h
+if ! ENABLE_MD5_EXT
 libmicrohttpd_la_SOURCES += \
   md5.c md5.h
+else
+libmicrohttpd_la_SOURCES += \
+  md5_ext.c md5_ext.h
+endif
 endif
 if ENABLE_SHA256
+libmicrohttpd_la_SOURCES += \
+  mhd_sha256_wrap.h
+if ! ENABLE_SHA256_EXT
 libmicrohttpd_la_SOURCES += \
   sha256.c sha256.h
+else
+libmicrohttpd_la_SOURCES += \
+  sha256_ext.c sha256_ext.h
+endif
 endif
 if ENABLE_SHA512_256
 libmicrohttpd_la_SOURCES += \
@@ -462,12 +476,40 @@ test_http_reasons_SOURCES = \
   reason_phrase.c mhd_str.c mhd_str.h
 
 test_md5_SOURCES = \
-  test_md5.c test_helpers.h \
+  test_md5.c test_helpers.h mhd_md5_wrap.h ../include/mhd_options.h
+if ! ENABLE_MD5_EXT
+test_md5_SOURCES += \
   md5.c md5.h mhd_bithelpers.h mhd_byteorder.h mhd_align.h
+test_md5_CPPFLAGS = $(AM_CPPFLAGS)
+test_md5_CFLAGS = $(AM_CFLAGS)
+test_md5_LDFLAGS = $(AM_LDFLAGS)
+test_md5_LDADD = $(LDADD)
+else
+test_md5_SOURCES += \
+  md5_ext.c md5_ext.h
+test_md5_CPPFLAGS = $(AM_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS)
+test_md5_CFLAGS = $(AM_CFLAGS) $(MHD_TLS_LIB_CFLAGS)
+test_md5_LDFLAGS = $(AM_LDFLAGS) $(MHD_TLS_LIB_LDFLAGS)
+test_md5_LDADD = $(MHD_TLS_LIBDEPS) $(LDADD)
+endif
 
 test_sha256_SOURCES = \
-  test_sha256.c test_helpers.h \
+  test_sha256.c test_helpers.h mhd_sha256_wrap.h ../include/mhd_options.h
+if ! ENABLE_SHA256_EXT
+test_sha256_SOURCES += \
   sha256.c sha256.h mhd_bithelpers.h mhd_byteorder.h mhd_align.h
+test_sha256_CPPFLAGS = $(AM_CPPFLAGS)
+test_sha256_CFLAGS = $(AM_CFLAGS)
+test_sha256_LDFLAGS = $(AM_LDFLAGS)
+test_sha256_LDADD = $(LDADD)
+else
+test_sha256_SOURCES += \
+  sha256_ext.c sha256_ext.h
+test_sha256_CPPFLAGS = $(AM_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS)
+test_sha256_CFLAGS = $(AM_CFLAGS) $(MHD_TLS_LIB_CFLAGS)
+test_sha256_LDFLAGS = $(AM_LDFLAGS) $(MHD_TLS_LIB_LDFLAGS)
+test_sha256_LDADD = $(MHD_TLS_LIBDEPS) $(LDADD)
+endif
 
 test_sha512_256_SOURCES = \
   test_sha512_256.c test_helpers.h \
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index bb9daa52..29ce1ccc 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -8465,6 +8465,12 @@ MHD_is_feature_supported (enum MHD_FEATURE feature)
 #else
     return MHD_NO;
 #endif
+  case MHD_FEATURE_EXTERN_HASH:
+#if defined(MHD_MD5_TLSLIB) || defined(MHD_SHA256_TLSLIB)
+    return MHD_YES;
+#else
+    return MHD_NO;
+#endif
 
   default:
     break;
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index 88668d1d..926ac5f4 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -34,10 +34,10 @@
 #include "internal.h"
 #include "response.h"
 #ifdef MHD_MD5_SUPPORT
-#  include "md5.h"
+#  include "mhd_md5_wrap.h"
 #endif /* MHD_MD5_SUPPORT */
 #ifdef MHD_SHA256_SUPPORT
-#  include "sha256.h"
+#  include "mhd_sha256_wrap.h"
 #endif /* MHD_SHA256_SUPPORT */
 #ifdef MHD_SHA512_256_SUPPORT
 #  include "sha512_256.h"
@@ -304,10 +304,10 @@ MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3)
 union DigestCtx
 {
 #ifdef MHD_MD5_SUPPORT
-  struct Md5Ctx md5_ctx;
+  struct Md5CtxWr md5_ctx;
 #endif /* MHD_MD5_SUPPORT */
 #ifdef MHD_SHA256_SUPPORT
-  struct Sha256Ctx sha256_ctx;
+  struct Sha256CtxWr sha256_ctx;
 #endif /* MHD_SHA256_SUPPORT */
 #ifdef MHD_SHA512_256_SUPPORT
   struct Sha512_256Ctx sha512_256_ctx;
@@ -334,9 +334,10 @@ struct DigestAlgorithm
    * Buffer for hex-print of the final digest.
    */
 #if _DEBUG
-  bool setup; /**< The structure was set-up */
-  bool inited; /**< The calculation was initialised */
-  bool hashing; /**< Some data has been hashed, but digest is not yet 
finalised */
+  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 */
 };
 
@@ -349,7 +350,8 @@ struct DigestAlgorithm
 _MHD_static_inline unsigned int
 digest_get_size (struct DigestAlgorithm *da)
 {
-  mhd_assert (da->setup);
+  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;
@@ -367,90 +369,136 @@ digest_get_size (struct DigestAlgorithm *da)
 }
 
 
+#if defined(MHD_MD5_HAS_DEINIT) || defined(MHD_SHA256_HAS_DEINIT)
 /**
- * Set-up the digest calculation structure.
- * @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.
+ * Indicates presence of digest_deinit() function
  */
-_MHD_static_inline bool
-digest_setup (struct DigestAlgorithm *da,
-              enum MHD_DigestBaseAlgo algo)
+#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->setup = false;
-  da->inited = false;
+  da->uninitialised = false;
+  da->algo_selected = false;
+  da->ready_for_hashing = false;
   da->hashing = false;
 #endif /* _DEBUG */
-  if (false
-#ifdef MHD_MD5_SUPPORT
-      || (MHD_DIGEST_BASE_ALGO_MD5 == algo)
-#endif /* MHD_MD5_SUPPORT */
-#ifdef MHD_SHA256_SUPPORT
-      || (MHD_DIGEST_BASE_ALGO_SHA256 == algo)
-#endif /* MHD_SHA256_SUPPORT */
-#ifdef MHD_SHA512_256_SUPPORT
-      || (MHD_DIGEST_BASE_ALGO_SHA512_256 == algo)
-#endif /* MHD_SHA512_256_SUPPORT */
-      )
-  {
-    da->algo = algo;
-#ifdef _DEBUG
-    da->setup = true;
-#endif /* _DEBUG */
-    return true;
-  }
-  return false; /* Bad or unsupported algorithm */
+  da->algo = MHD_DIGEST_BASE_ALGO_INVALID;
 }
 
 
 /**
- * Initialise/reset the digest calculation structure.
- * @param da the structure to initialise/reset
+ * 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_init (struct DigestAlgorithm *da)
+digest_deinit (struct DigestAlgorithm *da)
 {
-  mhd_assert (da->setup);
-  mhd_assert (! da->hashing);
-  mhd_assert (! da->inited);
-#ifdef MHD_MD5_SUPPORT
+  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)
   {
-    MHD_MD5_init (&da->ctx.md5_ctx);
+    da->algo = MHD_DIGEST_BASE_ALGO_MD5;
 #ifdef _DEBUG
-    da->inited = true;
+    da->algo_selected = true;
 #endif
+    MHD_MD5_init_one_time (&da->ctx.md5_ctx);
+#ifdef _DEBUG
+    da->ready_for_hashing = true;
+#endif
+    return true;
   }
-  else
 #endif /* MHD_MD5_SUPPORT */
 #ifdef MHD_SHA256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
+  if (MHD_DIGEST_BASE_ALGO_SHA256 == algo)
   {
-    MHD_SHA256_init (&da->ctx.sha256_ctx);
+    da->algo = MHD_DIGEST_BASE_ALGO_SHA256;
 #ifdef _DEBUG
-    da->inited = true;
+    da->algo_selected = true;
 #endif
+    MHD_SHA256_init_one_time (&da->ctx.sha256_ctx);
+#ifdef _DEBUG
+    da->ready_for_hashing = true;
+#endif
+    return true;
   }
-  else
 #endif /* MHD_SHA256_SUPPORT */
 #ifdef MHD_SHA512_256_SUPPORT
-  if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
+  if (MHD_DIGEST_BASE_ALGO_SHA512_256 == algo)
   {
-    MHD_SHA512_256_init (&da->ctx.sha512_256_ctx);
+    da->algo = MHD_DIGEST_BASE_ALGO_SHA512_256;
 #ifdef _DEBUG
-    da->inited = true;
+    da->algo_selected = true;
 #endif
-  }
-  else
-#endif /* MHD_SHA512_256_SUPPORT */
-  {
+    MHD_SHA512_256_init (&da->ctx.sha512_256_ctx);
 #ifdef _DEBUG
-    da->inited = false;
+    da->ready_for_hashing = true;
 #endif
-    mhd_assert (0); /* Bad algorithm */
+    return true;
   }
+#endif /* MHD_SHA512_256_SUPPORT */
+
+  da->algo = MHD_DIGEST_BASE_ALGO_INVALID;
+  return false; /* Unsupported or bad algorithm */
 }
 
 
@@ -465,7 +513,9 @@ digest_update (struct DigestAlgorithm *da,
                const void *data,
                size_t length)
 {
-  mhd_assert (da->inited);
+  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);
@@ -525,30 +575,160 @@ digest_update_with_colon (struct DigestAlgorithm *da)
 _MHD_static_inline void
 digest_calc_hash (struct DigestAlgorithm *da, uint8_t *digest)
 {
-  mhd_assert (da->inited);
+  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);   /* May not happen */
+  mhd_assert (0);   /* Should not happen */
 #ifdef _DEBUG
   da->hashing = false;
-  da->inited = 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
@@ -1314,7 +1494,7 @@ calculate_nonce (uint64_t nonce_time,
                  struct DigestAlgorithm *da,
                  char *nonce)
 {
-  digest_init (da);
+  mhd_assert (! da->hashing);
   if (1)
   {
     /* Add the timestamp to the hash calculation */
@@ -1522,6 +1702,7 @@ calculate_add_nonce (struct MHD_Connection *const 
connection,
   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);
 
@@ -1541,6 +1722,11 @@ calculate_add_nonce (struct MHD_Connection *const 
connection,
                    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;
 
@@ -1589,6 +1775,7 @@ calculate_add_nonce_with_retry (struct MHD_Connection 
*const connection,
 {
   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)
@@ -1612,6 +1799,10 @@ calculate_add_nonce_with_retry (struct MHD_Connection 
*const connection,
     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 */
 
@@ -1652,6 +1843,7 @@ calculate_add_nonce_with_retry (struct MHD_Connection 
*const connection,
       if (timestamp1 == timestamp2)
         timestamp2 -= 2; /* Fallback value */
     }
+    digest_reset (da);
     if (! calculate_add_nonce (connection, timestamp2, realm, realm_len, da,
                                nonce2))
     {
@@ -1690,7 +1882,7 @@ calc_userdigest (struct DigestAlgorithm *da,
                  const char *password,
                  uint8_t *ha1_bin)
 {
-  digest_init (da);
+  mhd_assert (! da->hashing);
   digest_update (da, username, username_len);
   digest_update_with_colon (da);
   digest_update (da, realm, realm_len);
@@ -1725,8 +1917,10 @@ calc_userdigest (struct DigestAlgorithm *da,
  *                            userdigest upon return
  * @param userdigest_bin the size of the @a userdigest_bin buffer, must be
  *                       at least #MHD_digest_get_hash_size(algo3) bytes long
- * @return MHD_YES on success, MHD_NO if @a userdigest_bin is too small or
- *                             if @a algo3 algorithm is not supported.
+ * @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 0x00097535
  * @ingroup authentication
@@ -1740,18 +1934,31 @@ MHD_digest_auth_calc_userdigest (enum 
MHD_DigestAuthAlgo3 algo3,
                                  size_t bin_buf_size)
 {
   struct DigestAlgorithm da;
-  if (! digest_setup (&da, get_base_digest_algo (algo3)))
+  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)
-    return MHD_NO;
-  calc_userdigest (&da,
-                   username,
-                   strlen (username),
-                   realm,
-                   strlen (realm),
-                   password,
-                   userdigest_bin);
-  return MHD_YES;
+    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;
 }
 
 
@@ -1775,7 +1982,7 @@ calc_userhash (struct DigestAlgorithm *da,
                uint8_t *digest_bin)
 {
   mhd_assert (NULL != username);
-  digest_init (da);
+  mhd_assert (! da->hashing);
   digest_update (da, username, username_len);
   digest_update_with_colon (da);
   digest_update (da, realm, realm_len);
@@ -1815,8 +2022,10 @@ calc_userhash (struct DigestAlgorithm *da,
  *                          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.
+ * @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)
  * @note Available since #MHD_VERSION 0x00097535
  * @ingroup authentication
  */
@@ -1828,17 +2037,30 @@ MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 
algo3,
                                size_t bin_buf_size)
 {
   struct DigestAlgorithm da;
-  if (! digest_setup (&da, get_base_digest_algo (algo3)))
+  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)
-    return MHD_NO;
-  calc_userhash (&da,
-                 username,
-                 strlen (username),
-                 realm,
-                 strlen (realm),
-                 userhash_bin);
-  return MHD_YES;
+    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;
 }
 
 
@@ -1874,8 +2096,10 @@ MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 
algo3,
  *                          userhash string
  * @param bin_buf_size the size of the @a userhash_bin buffer, must be
  *                     at least #MHD_digest_get_hash_size(algo3)*2+1 chars long
- * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or
- *                             if @a algo3 algorithm is not supported.
+ * @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).
  * @note Available since #MHD_VERSION 0x00097535
  * @ingroup authentication
  */
@@ -2302,12 +2526,12 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
                              uint32_t max_nc,
                              enum MHD_DigestAuthMultiQOP mqop,
                              enum MHD_DigestAuthMultiAlgo3 malgo3,
-                             char **pbuf)
+                             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 */
-  struct DigestAlgorithm da;
   unsigned int digest_size;
   uint8_t hash1_bin[MAX_DIGEST];
   uint8_t hash2_bin[MAX_DIGEST];
@@ -2383,7 +2607,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     return MHD_DAUTH_WRONG_ALGO;
   }
 #endif /* ! MHD_SHA512_256_SUPPORT */
-  if (! digest_setup (&da, get_base_digest_algo (c_algo)))
+  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;
@@ -2407,7 +2631,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
                  "non-standard extension.\n"));
 #endif /* HAVE_MESSAGES */
 
-  digest_size = digest_get_size (&da);
+  digest_size = digest_get_size (da);
 
   /* ** A quick check for presence of all required parameters ** */
 
@@ -2525,11 +2749,18 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   else
   { /* Userhash */
     mhd_assert (NULL != params->username.value.str);
-    calc_userhash (&da, username, username_len, realm, realm_len, hash1_bin);
+    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 */
 
@@ -2643,9 +2874,9 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   /* ** Build H(A2) and check URI match in the header and in the request ** */
 
   /* Get 'uri' */
-  digest_init (&da);
-  digest_update_str (&da, connection->rq.method);
-  digest_update_with_colon (&da);
+  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);
@@ -2656,33 +2887,45 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
 
-  digest_update (&da, unq_copy.str, unq_copy.len);
+  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);
+  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)
-    calc_userdigest (&da,
+  {
+    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' ** */
 
-  digest_init (&da);
+  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);
+  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. */
@@ -2694,15 +2937,15 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     return MHD_DAUTH_RESPONSE_WRONG;
 
   /* Update digest with ':' */
-  digest_update_with_colon (&da);
+  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);
+  digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
   /* Update digest with ':' */
-  digest_update_with_colon (&da);
+  digest_update_with_colon (da);
   if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
   {
     /* Update digest with 'nc' text value */
@@ -2710,33 +2953,37 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
                                   &unquoted);
     if (_MHD_UNQ_OK != unq_res)
       return MHD_DAUTH_ERROR;
-    digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len);
+    digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
     /* Update digest with ':' */
-    digest_update_with_colon (&da);
+    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);
+    digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
     /* Update digest with ':' */
-    digest_update_with_colon (&da);
+    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);
+    digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
     /* Update digest with ':' */
-    digest_update_with_colon (&da);
+    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);
+  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);
+  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;
@@ -2746,6 +2993,8 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     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,
@@ -2759,9 +3008,13 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
                      realm,
                      realm_len,
                      daemon->dauth_bind_type,
-                     &da,
+                     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)))
@@ -2816,13 +3069,16 @@ digest_auth_check_all (struct MHD_Connection 
*connection,
 {
   enum MHD_DigestAuthResult res;
   char *buf;
+  struct DigestAlgorithm da;
 
   buf = NULL;
+  digest_setup_zero (&da);
   res = digest_auth_check_all_inner (connection, realm, username, password,
                                      userdigest,
                                      nonce_timeout,
                                      max_nc, mqop, malgo3,
-                                     &buf);
+                                     &buf, &da);
+  digest_deinit (&da);
   if (NULL != buf)
     free (buf);
 
@@ -3173,20 +3429,8 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
 
 
 /**
- * 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.
+ * 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
@@ -3226,21 +3470,26 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
  * @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 0x00097526
  * @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)
+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=\"";
@@ -3258,7 +3507,6 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
   size_t buf_size;
   char *buf;
   size_t p; /* The position in the buffer */
-  struct DigestAlgorithm da;
 
   if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION))
   {
@@ -3302,7 +3550,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
       (((unsigned int) mqop) & MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT))
     MHD_PANIC (_ ("Wrong 'mqop' value, API violation"));
 
-  if (! digest_setup (&da, get_base_digest_algo (s_algo)))
+  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)
@@ -3348,6 +3596,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
   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)
@@ -3379,7 +3628,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
   }
   /* 'nonce="xxxx", ' */
   buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */
-  buf_size += NONCE_STD_LEN (digest_get_size (&da)); /* Escaping not needed */
+  buf_size += NONCE_STD_LEN (digest_get_size (da)); /* Escaping not needed */
   /* 'opaque="xxxx", ' */
   if (NULL != opaque)
   {
@@ -3388,6 +3637,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
     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
@@ -3400,6 +3650,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
     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
@@ -3421,6 +3672,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
   buf = malloc (buf_size);
   if (NULL == buf)
     return MHD_NO;
+  *buf_ptr = buf;
 
   /* Build the challenge string */
   p = 0;
@@ -3444,7 +3696,6 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
       MHD_DLOG (connection->daemon,
                 _ ("The 'realm' is too large after 'quoting'.\n"));
 #endif /* HAVE_MESSAGES */
-      free (buf);
       return MHD_NO;
     }
     p += quoted_size;
@@ -3507,9 +3758,20 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
   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))
+  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 "
@@ -3518,7 +3780,7 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
 #endif /* HAVE_MESSAGES */
     (void) 0; /* Mute compiler warning for builds without messages */
   }
-  p += NONCE_STD_LEN (digest_get_size (&da));
+  p += NONCE_STD_LEN (digest_get_size (da));
   buf[p++] = '\"';
   buf[p++] = ',';
   buf[p++] = ' ';
@@ -3588,15 +3850,108 @@ MHD_queue_auth_required_response3 (struct 
MHD_Connection *connection,
     MHD_DLOG (connection->daemon,
               _ ("Failed to add Digest auth header.\n"));
 #endif /* HAVE_MESSAGES */
-    free (buf);
     return MHD_NO;
   }
-  free (buf);
 
   return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
 }
 
 
+/**
+ * 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)
+ * @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
+ * @return #MHD_YES on success, #MHD_NO otherwise
+ * @note Available since #MHD_VERSION 0x00097526
+ * @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
  *
diff --git a/src/microhttpd/md5.h b/src/microhttpd/md5.h
index 7d522254..71cb29f3 100644
--- a/src/microhttpd/md5.h
+++ b/src/microhttpd/md5.h
@@ -123,4 +123,9 @@ 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 */
diff --git a/src/microhttpd/md5_ext.c b/src/microhttpd/md5_ext.c
new file mode 100644
index 00000000..5466f704
--- /dev/null
+++ b/src/microhttpd/md5_ext.c
@@ -0,0 +1,95 @@
+/*
+     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/md5_ext.h
+ * @brief  Wrapper for MD5 calculation performed by TLS library
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <gnutls/crypto.h>
+#include "md5_ext.h"
+#include "mhd_assert.h"
+
+
+/**
+ * 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)
+{
+  ctx->handle = NULL;
+  ctx->ext_error = gnutls_hash_init (&ctx->handle, GNUTLS_DIG_MD5);
+  if ((0 != ctx->ext_error) && (NULL != ctx->handle))
+  {
+    gnutls_free (ctx->handle);
+    ctx->handle = NULL;
+  }
+  else
+    mhd_assert (NULL != ctx->handle);
+}
+
+
+/**
+ * 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)
+{
+  if (0 == ctx->ext_error)
+    ctx->ext_error = gnutls_hash (ctx->handle, data, length);
+}
+
+
+/**
+ * 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])
+{
+  if (0 == ctx->ext_error)
+    gnutls_hash_output (ctx->handle, digest);
+}
+
+
+/**
+ * Free allocated resources.
+ *
+ * @param ctx the calculation context
+ */
+void
+MHD_MD5_deinit (struct Md5CtxExt *ctx)
+{
+  if (NULL != ctx->handle)
+    gnutls_hash_deinit (ctx->handle, NULL);
+}
diff --git a/src/microhttpd/md5_ext.h b/src/microhttpd/md5_ext.h
new file mode 100644
index 00000000..b7437c03
--- /dev/null
+++ b/src/microhttpd/md5_ext.h
@@ -0,0 +1,115 @@
+/*
+     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/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 */
diff --git a/src/microhttpd/mhd_md5_wrap.h b/src/microhttpd/mhd_md5_wrap.h
new file mode 100644
index 00000000..98a5d958
--- /dev/null
+++ b/src/microhttpd/mhd_md5_wrap.h
@@ -0,0 +1,98 @@
+/*
+     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 */
diff --git a/src/microhttpd/mhd_sha256_wrap.h b/src/microhttpd/mhd_sha256_wrap.h
new file mode 100644
index 00000000..5879c2ca
--- /dev/null
+++ b/src/microhttpd/mhd_sha256_wrap.h
@@ -0,0 +1,100 @@
+/*
+     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 */
diff --git a/src/microhttpd/sha256.h b/src/microhttpd/sha256.h
index c3d32e9c..9069a59a 100644
--- a/src/microhttpd/sha256.h
+++ b/src/microhttpd/sha256.h
@@ -114,4 +114,9 @@ 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 */
diff --git a/src/microhttpd/sha256_ext.c b/src/microhttpd/sha256_ext.c
new file mode 100644
index 00000000..d1c2a6ed
--- /dev/null
+++ b/src/microhttpd/sha256_ext.c
@@ -0,0 +1,95 @@
+/*
+     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/sha256_ext.h
+ * @brief  Wrapper for SHA-256 calculation performed by TLS library
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <gnutls/crypto.h>
+#include "sha256_ext.h"
+#include "mhd_assert.h"
+
+
+/**
+ * Initialise structure for SHA-256 calculation, allocate resources.
+ *
+ * This function must not be called more than one time for @a ctx.
+ *
+ * @param ctx the calculation context
+ */
+void
+MHD_SHA256_init_one_time (struct Sha256CtxExt *ctx)
+{
+  ctx->handle = NULL;
+  ctx->ext_error = gnutls_hash_init (&ctx->handle, GNUTLS_DIG_SHA256);
+  if ((0 != ctx->ext_error) && (NULL != ctx->handle))
+  {
+    gnutls_free (ctx->handle);
+    ctx->handle = NULL;
+  }
+  else
+    mhd_assert (NULL != ctx->handle);
+}
+
+
+/**
+ * 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_SHA256_update (struct Sha256CtxExt *ctx,
+                   const uint8_t *data,
+                   size_t length)
+{
+  if (0 == ctx->ext_error)
+    ctx->ext_error = gnutls_hash (ctx->handle, data, length);
+}
+
+
+/**
+ * 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
+ */
+void
+MHD_SHA256_finish_reset (struct Sha256CtxExt *ctx,
+                         uint8_t digest[SHA256_DIGEST_SIZE])
+{
+  if (0 == ctx->ext_error)
+    gnutls_hash_output (ctx->handle, digest);
+}
+
+
+/**
+ * Free allocated resources.
+ *
+ * @param ctx the calculation context
+ */
+void
+MHD_SHA256_deinit (struct Sha256CtxExt *ctx)
+{
+  if (NULL != ctx->handle)
+    gnutls_hash_deinit (ctx->handle, NULL);
+}
diff --git a/src/microhttpd/sha256_ext.h b/src/microhttpd/sha256_ext.h
new file mode 100644
index 00000000..7d2ee6a5
--- /dev/null
+++ b/src/microhttpd/sha256_ext.h
@@ -0,0 +1,115 @@
+/*
+     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/sha256_ext.h
+ * @brief  Wrapper declarations for SHA-256 calculation performed by TLS 
library
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA256_EXT_H
+#define MHD_SHA256_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 SHA-256 resulting digest in bytes
+ * This is the final digest size, not intermediate hash.
+ */
+#define SHA256_DIGEST_SIZE (32)
+
+/* Actual declaration is in GnuTLS lib header */
+struct hash_hd_st;
+
+/**
+ * Indicates that struct Sha256CtxExt has 'ext_error'
+ */
+#define MHD_SHA256_HAS_EXT_ERROR 1
+
+/**
+ * SHA-256 calculation context
+ */
+struct 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.
+ */
+#define MHD_SHA256_HAS_INIT_ONE_TIME 1
+
+/**
+ * Initialise structure for SHA-256 calculation, allocate resources.
+ *
+ * This function must not be called more than one time for @a ctx.
+ *
+ * @param ctx the calculation context
+ */
+void
+MHD_SHA256_init_one_time (struct Sha256CtxExt *ctx);
+
+
+/**
+ * SHA-256 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_SHA256_update (struct Sha256CtxExt *ctx,
+                   const uint8_t *data,
+                   size_t length);
+
+
+/**
+ * Indicates that MHD_SHA256_finish_reset() function is available
+ */
+#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
+ */
+void
+MHD_SHA256_finish_reset (struct Sha256CtxExt *ctx,
+                         uint8_t digest[SHA256_DIGEST_SIZE]);
+
+/**
+ * Indicates that MHD_SHA256_deinit() function is present
+ */
+#define MHD_SHA256_HAS_DEINIT 1
+
+/**
+ * Free allocated resources.
+ *
+ * @param ctx the calculation context
+ */
+void
+MHD_SHA256_deinit (struct Sha256CtxExt *ctx);
+
+#endif /* MHD_SHA256_EXT_H */
diff --git a/src/microhttpd/test_dauth_userdigest.c 
b/src/microhttpd/test_dauth_userdigest.c
index 6b15a282..f467be6d 100644
--- a/src/microhttpd/test_dauth_userdigest.c
+++ b/src/microhttpd/test_dauth_userdigest.c
@@ -23,12 +23,19 @@
  * @author Karlson2k (Evgeny Grin)
  */
 
+#include "mhd_options.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "microhttpd.h"
 #include "test_helpers.h"
 
+#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
+  (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
+#define NEED_GCRYP_INIT 1
+#include <gcrypt.h>
+#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
+
 static int verbose = 1; /* verbose level (0-1)*/
 
 /* Declarations and data */
@@ -619,6 +626,13 @@ main (int argc, char *argv[])
   if (has_param (argc, argv, "-s") || has_param (argc, argv, "--silent"))
     verbose = 0;
 
+#ifdef NEED_GCRYP_INIT
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif /* GCRYCTL_INITIALIZATION_FINISHED */
+#endif /* NEED_GCRYP_INIT */
+
   if (MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
     num_failed += test_md5 ();
   num_failed += test_md5_failure ();
diff --git a/src/microhttpd/test_dauth_userhash.c 
b/src/microhttpd/test_dauth_userhash.c
index 633ab4b9..c145beb0 100644
--- a/src/microhttpd/test_dauth_userhash.c
+++ b/src/microhttpd/test_dauth_userhash.c
@@ -26,9 +26,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include "mhd_options.h"
 #include "microhttpd.h"
 #include "test_helpers.h"
 
+#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
+  (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
+#define NEED_GCRYP_INIT 1
+#include <gcrypt.h>
+#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
+
 static int verbose = 1; /* verbose level (0-1)*/
 
 /* Declarations and data */
@@ -748,6 +755,13 @@ main (int argc, char *argv[])
   if (has_param (argc, argv, "-s") || has_param (argc, argv, "--silent"))
     verbose = 0;
 
+#ifdef NEED_GCRYP_INIT
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif /* GCRYCTL_INITIALIZATION_FINISHED */
+#endif /* NEED_GCRYP_INIT */
+
   if (MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
     num_failed += test_md5 ();
   num_failed += test_md5_failure ();
diff --git a/src/microhttpd/test_md5.c b/src/microhttpd/test_md5.c
index 13f9d46e..e6addab3 100644
--- a/src/microhttpd/test_md5.c
+++ b/src/microhttpd/test_md5.c
@@ -24,11 +24,16 @@
  */
 
 #include "mhd_options.h"
-#include "md5.h"
+#include "mhd_md5_wrap.h"
 #include "test_helpers.h"
 #include <stdio.h>
 #include <stdlib.h>
 
+#if defined(MHD_MD5_TLSLIB) && defined(MHD_HTTPS_REQUIRE_GCRYPT)
+#define NEED_GCRYP_INIT 1
+#include <gcrypt.h>
+#endif /* MHD_MD5_TLSLIB && MHD_HTTPS_REQUIRE_GCRYPT */
+
 static int verbose = 0; /* verbose level (0-1)*/
 
 
@@ -293,9 +298,9 @@ check_result (const char *test_name,
   {
     char calc_str[MD5_DIGEST_STRING_SIZE];
     bin2hex (calculated, MD5_DIGEST_SIZE, calc_str);
-    printf (
-      "PASSED: %s check %u: calculated digest %s match expected digest.\n",
-      test_name, check_num, calc_str);
+    printf ("PASSED: %s check %u: calculated digest %s "
+            "matches expected digest.\n",
+            test_name, check_num, calc_str);
     fflush (stdout);
   }
   return failed ? 1 : 0;
@@ -312,19 +317,28 @@ test1_str (void)
 {
   unsigned int i;
   int num_failed = 0;
-  struct Md5Ctx ctx;
+  struct Md5CtxWr ctx;
+
+  MHD_MD5_init_one_time (&ctx);
 
   for (i = 0; i < units1_num; i++)
   {
     uint8_t digest[MD5_DIGEST_SIZE];
 
-    MHD_MD5_init (&ctx);
     MHD_MD5_update (&ctx, (const uint8_t *) data_units1[i].str_l.str,
                     data_units1[i].str_l.len);
-    MHD_MD5_finish (&ctx, digest);
+    MHD_MD5_finish_reset (&ctx, digest);
+#ifdef MHD_MD5_HAS_EXT_ERROR
+    if (0 != ctx.ext_error)
+    {
+      fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error);
+      exit (99);
+    }
+#endif
     num_failed += check_result (__FUNCTION__, i, digest,
                                 data_units1[i].digest);
   }
+  MHD_MD5_deinit (&ctx);
   return num_failed;
 }
 
@@ -334,18 +348,27 @@ test1_bin (void)
 {
   unsigned int i;
   int num_failed = 0;
-  struct Md5Ctx ctx;
+  struct Md5CtxWr ctx;
+
+  MHD_MD5_init_one_time (&ctx);
 
   for (i = 0; i < units2_num; i++)
   {
     uint8_t digest[MD5_DIGEST_SIZE];
 
-    MHD_MD5_init (&ctx);
     MHD_MD5_update (&ctx, data_units2[i].bin_l.bin, data_units2[i].bin_l.len);
-    MHD_MD5_finish (&ctx, digest);
+    MHD_MD5_finish_reset (&ctx, digest);
+#ifdef MHD_MD5_HAS_EXT_ERROR
+    if (0 != ctx.ext_error)
+    {
+      fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error);
+      exit (99);
+    }
+#endif
     num_failed += check_result (__FUNCTION__, i, digest,
                                 data_units2[i].digest);
   }
+  MHD_MD5_deinit (&ctx);
   return num_failed;
 }
 
@@ -356,24 +379,33 @@ test2_str (void)
 {
   unsigned int i;
   int num_failed = 0;
-  struct Md5Ctx ctx;
+  struct Md5CtxWr ctx;
+
+  MHD_MD5_init_one_time (&ctx);
 
   for (i = 0; i < units1_num; i++)
   {
     uint8_t digest[MD5_DIGEST_SIZE];
     size_t part_s = data_units1[i].str_l.len / 4;
 
-    MHD_MD5_init (&ctx);
     MHD_MD5_update (&ctx, (const uint8_t *) "", 0);
     MHD_MD5_update (&ctx, (const uint8_t *) data_units1[i].str_l.str, part_s);
     MHD_MD5_update (&ctx, (const uint8_t *) "", 0);
     MHD_MD5_update (&ctx, (const uint8_t *) data_units1[i].str_l.str + part_s,
                     data_units1[i].str_l.len - part_s);
     MHD_MD5_update (&ctx, (const uint8_t *) "", 0);
-    MHD_MD5_finish (&ctx, digest);
+    MHD_MD5_finish_reset (&ctx, digest);
+#ifdef MHD_MD5_HAS_EXT_ERROR
+    if (0 != ctx.ext_error)
+    {
+      fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error);
+      exit (99);
+    }
+#endif
     num_failed += check_result (__FUNCTION__, i, digest,
                                 data_units1[i].digest);
   }
+  MHD_MD5_deinit (&ctx);
   return num_failed;
 }
 
@@ -383,22 +415,31 @@ test2_bin (void)
 {
   unsigned int i;
   int num_failed = 0;
-  struct Md5Ctx ctx;
+  struct Md5CtxWr ctx;
+
+  MHD_MD5_init_one_time (&ctx);
 
   for (i = 0; i < units2_num; i++)
   {
     uint8_t digest[MD5_DIGEST_SIZE];
     size_t part_s = data_units2[i].bin_l.len * 2 / 3;
 
-    MHD_MD5_init (&ctx);
     MHD_MD5_update (&ctx, data_units2[i].bin_l.bin, part_s);
     MHD_MD5_update (&ctx, (const uint8_t *) "", 0);
     MHD_MD5_update (&ctx, data_units2[i].bin_l.bin + part_s,
                     data_units2[i].bin_l.len - part_s);
-    MHD_MD5_finish (&ctx, digest);
+    MHD_MD5_finish_reset (&ctx, digest);
+#ifdef MHD_MD5_HAS_EXT_ERROR
+    if (0 != ctx.ext_error)
+    {
+      fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error);
+      exit (99);
+    }
+#endif
     num_failed += check_result (__FUNCTION__, i, digest,
                                 data_units2[i].digest);
   }
+  MHD_MD5_deinit (&ctx);
   return num_failed;
 }
 
@@ -414,7 +455,7 @@ test_unaligned (void)
   unsigned int offset;
   uint8_t *buf;
   uint8_t *digest_buf;
-  struct Md5Ctx ctx;
+  struct Md5CtxWr ctx;
 
   const struct data_unit2 *const tdata = data_units2 + DATA_POS;
 
@@ -423,6 +464,8 @@ test_unaligned (void)
   if ((NULL == buf) || (NULL == digest_buf))
     exit (99);
 
+  MHD_MD5_init_one_time (&ctx);
+
   for (offset = MAX_OFFSET; offset >= 1; --offset)
   {
     uint8_t *unaligned_digest;
@@ -433,12 +476,19 @@ test_unaligned (void)
     unaligned_digest = digest_buf + MAX_OFFSET - offset;
     memset (unaligned_digest, 0, MD5_DIGEST_SIZE);
 
-    MHD_MD5_init (&ctx);
     MHD_MD5_update (&ctx, unaligned_buf, tdata->bin_l.len);
-    MHD_MD5_finish (&ctx, unaligned_digest);
+    MHD_MD5_finish_reset (&ctx, unaligned_digest);
+#ifdef MHD_MD5_HAS_EXT_ERROR
+    if (0 != ctx.ext_error)
+    {
+      fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error);
+      exit (99);
+    }
+#endif
     num_failed += check_result (__FUNCTION__, MAX_OFFSET - offset,
                                 unaligned_digest, tdata->digest);
   }
+  MHD_MD5_deinit (&ctx);
   free (digest_buf);
   free (buf);
   return num_failed;
@@ -453,6 +503,13 @@ main (int argc, char *argv[])
   if (has_param (argc, argv, "-v") || has_param (argc, argv, "--verbose"))
     verbose = 1;
 
+#ifdef NEED_GCRYP_INIT
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif /* GCRYCTL_INITIALIZATION_FINISHED */
+#endif /* NEED_GCRYP_INIT */
+
   num_failed += test1_str ();
   num_failed += test1_bin ();
 
diff --git a/src/microhttpd/test_sha256.c b/src/microhttpd/test_sha256.c
index f287afd6..5f6559e2 100644
--- a/src/microhttpd/test_sha256.c
+++ b/src/microhttpd/test_sha256.c
@@ -24,11 +24,16 @@
  */
 
 #include "mhd_options.h"
-#include "sha256.h"
+#include "mhd_sha256_wrap.h"
 #include "test_helpers.h"
 #include <stdio.h>
 #include <stdlib.h>
 
+#if defined(MHD_SHA256_TLSLIB) && defined(MHD_HTTPS_REQUIRE_GCRYPT)
+#define NEED_GCRYP_INIT 1
+#include <gcrypt.h>
+#endif /* MHD_SHA256_TLSLIB && MHD_HTTPS_REQUIRE_GCRYPT */
+
 static int verbose = 0; /* verbose level (0-1)*/
 
 
@@ -320,9 +325,9 @@ check_result (const char *test_name,
   {
     char calc_str[SHA256_DIGEST_STRING_SIZE];
     bin2hex (calculated, SHA256_DIGEST_SIZE, calc_str);
-    printf (
-      "PASSED: %s check %u: calculated digest %s matches expected digest.\n",
-      test_name, check_num, calc_str);
+    printf ("PASSED: %s check %u: calculated digest %s "
+            "matches expected digest.\n",
+            test_name, check_num, calc_str);
     fflush (stdout);
   }
   return failed ? 1 : 0;
@@ -339,19 +344,27 @@ test1_str (void)
 {
   int num_failed = 0;
   unsigned int i;
-  struct Sha256Ctx ctx;
+  struct Sha256CtxWr ctx;
 
+  MHD_SHA256_init_one_time (&ctx);
   for (i = 0; i < units1_num; i++)
   {
     uint8_t digest[SHA256_DIGEST_SIZE];
 
-    MHD_SHA256_init (&ctx);
     MHD_SHA256_update (&ctx, (const uint8_t *) data_units1[i].str_l.str,
                        data_units1[i].str_l.len);
-    MHD_SHA256_finish (&ctx, digest);
+    MHD_SHA256_finish_reset (&ctx, digest);
+#ifdef MHD_SHA256_HAS_EXT_ERROR
+    if (0 != ctx.ext_error)
+    {
+      fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error);
+      exit (99);
+    }
+#endif /* MHD_SHA256_HAS_EXT_ERROR */
     num_failed += check_result (__FUNCTION__, i, digest,
                                 data_units1[i].digest);
   }
+  MHD_SHA256_deinit (&ctx);
   return num_failed;
 }
 
@@ -361,19 +374,27 @@ test1_bin (void)
 {
   int num_failed = 0;
   unsigned int i;
-  struct Sha256Ctx ctx;
+  struct Sha256CtxWr ctx;
 
+  MHD_SHA256_init_one_time (&ctx);
   for (i = 0; i < units2_num; i++)
   {
     uint8_t digest[SHA256_DIGEST_SIZE];
 
-    MHD_SHA256_init (&ctx);
     MHD_SHA256_update (&ctx, data_units2[i].bin_l.bin,
                        data_units2[i].bin_l.len);
-    MHD_SHA256_finish (&ctx, digest);
+    MHD_SHA256_finish_reset (&ctx, digest);
+#ifdef MHD_SHA256_HAS_EXT_ERROR
+    if (0 != ctx.ext_error)
+    {
+      fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error);
+      exit (99);
+    }
+#endif /* MHD_SHA256_HAS_EXT_ERROR */
     num_failed += check_result (__FUNCTION__, i, digest,
                                 data_units2[i].digest);
   }
+  MHD_SHA256_deinit (&ctx);
   return num_failed;
 }
 
@@ -384,14 +405,14 @@ test2_str (void)
 {
   int num_failed = 0;
   unsigned int i;
-  struct Sha256Ctx ctx;
+  struct Sha256CtxWr ctx;
 
+  MHD_SHA256_init_one_time (&ctx);
   for (i = 0; i < units1_num; i++)
   {
     uint8_t digest[SHA256_DIGEST_SIZE];
     size_t part_s = data_units1[i].str_l.len / 4;
 
-    MHD_SHA256_init (&ctx);
     MHD_SHA256_update (&ctx, (const uint8_t *) "", 0);
     MHD_SHA256_update (&ctx, (const uint8_t *) data_units1[i].str_l.str,
                        part_s);
@@ -400,10 +421,18 @@ test2_str (void)
                        + part_s,
                        data_units1[i].str_l.len - part_s);
     MHD_SHA256_update (&ctx, (const uint8_t *) "", 0);
-    MHD_SHA256_finish (&ctx, digest);
+    MHD_SHA256_finish_reset (&ctx, digest);
+#ifdef MHD_SHA256_HAS_EXT_ERROR
+    if (0 != ctx.ext_error)
+    {
+      fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error);
+      exit (99);
+    }
+#endif /* MHD_SHA256_HAS_EXT_ERROR */
     num_failed += check_result (__FUNCTION__, i, digest,
                                 data_units1[i].digest);
   }
+  MHD_SHA256_deinit (&ctx);
   return num_failed;
 }
 
@@ -413,22 +442,30 @@ test2_bin (void)
 {
   int num_failed = 0;
   unsigned int i;
-  struct Sha256Ctx ctx;
+  struct Sha256CtxWr ctx;
 
+  MHD_SHA256_init_one_time (&ctx);
   for (i = 0; i < units2_num; i++)
   {
     uint8_t digest[SHA256_DIGEST_SIZE];
     size_t part_s = data_units2[i].bin_l.len * 2 / 3;
 
-    MHD_SHA256_init (&ctx);
     MHD_SHA256_update (&ctx, data_units2[i].bin_l.bin, part_s);
     MHD_SHA256_update (&ctx, (const uint8_t *) "", 0);
     MHD_SHA256_update (&ctx, data_units2[i].bin_l.bin + part_s,
                        data_units2[i].bin_l.len - part_s);
-    MHD_SHA256_finish (&ctx, digest);
+    MHD_SHA256_finish_reset (&ctx, digest);
+#ifdef MHD_SHA256_HAS_EXT_ERROR
+    if (0 != ctx.ext_error)
+    {
+      fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error);
+      exit (99);
+    }
+#endif /* MHD_SHA256_HAS_EXT_ERROR */
     num_failed += check_result (__FUNCTION__, i, digest,
                                 data_units2[i].digest);
   }
+  MHD_SHA256_deinit (&ctx);
   return num_failed;
 }
 
@@ -444,10 +481,11 @@ test_unaligned (void)
   unsigned int offset;
   uint8_t *buf;
   uint8_t *digest_buf;
-  struct Sha256Ctx ctx;
+  struct Sha256CtxWr ctx;
 
   const struct data_unit2 *const tdata = data_units2 + DATA_POS;
 
+  MHD_SHA256_init_one_time (&ctx);
   buf = malloc (tdata->bin_l.len + MAX_OFFSET);
   digest_buf = malloc (SHA256_DIGEST_SIZE + MAX_OFFSET);
   if ((NULL == buf) || (NULL == digest_buf))
@@ -463,14 +501,21 @@ test_unaligned (void)
     unaligned_digest = digest_buf + MAX_OFFSET - offset;
     memset (unaligned_digest, 0, SHA256_DIGEST_SIZE);
 
-    MHD_SHA256_init (&ctx);
     MHD_SHA256_update (&ctx, unaligned_buf, tdata->bin_l.len);
-    MHD_SHA256_finish (&ctx, unaligned_digest);
+    MHD_SHA256_finish_reset (&ctx, unaligned_digest);
+#ifdef MHD_SHA256_HAS_EXT_ERROR
+    if (0 != ctx.ext_error)
+    {
+      fprintf (stderr, "External hashing error: %d.\n", ctx.ext_error);
+      exit (99);
+    }
+#endif /* MHD_SHA256_HAS_EXT_ERROR */
     num_failed += check_result (__FUNCTION__, MAX_OFFSET - offset,
                                 unaligned_digest, tdata->digest);
   }
   free (digest_buf);
   free (buf);
+  MHD_SHA256_deinit (&ctx);
   return num_failed;
 }
 
@@ -483,6 +528,13 @@ main (int argc, char *argv[])
   if (has_param (argc, argv, "-v") || has_param (argc, argv, "--verbose"))
     verbose = 1;
 
+#ifdef NEED_GCRYP_INIT
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif /* GCRYCTL_INITIALIZATION_FINISHED */
+#endif /* NEED_GCRYP_INIT */
+
   num_failed += test1_str ();
   num_failed += test1_bin ();
 
diff --git a/src/testcurl/test_digestauth.c b/src/testcurl/test_digestauth.c
index 387d8abf..adc5f330 100644
--- a/src/testcurl/test_digestauth.c
+++ b/src/testcurl/test_digestauth.c
@@ -26,18 +26,19 @@
  * @author Karlson2k (Evgeny Grin)
  */
 
-#include "MHD_config.h"
+#include "mhd_options.h"
 #include "platform.h"
 #include <curl/curl.h>
 #include <microhttpd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
-#ifdef MHD_HTTPS_REQUIRE_GCRYPT
-#ifdef HAVE_GCRYPT_H
+
+#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
+  (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
+#define NEED_GCRYP_INIT 1
 #include <gcrypt.h>
-#endif
-#endif /* MHD_HTTPS_REQUIRE_GCRYPT */
+#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
 
 #ifndef WINDOWS
 #include <sys/socket.h>
@@ -491,14 +492,12 @@ main (int argc, char *const *argv)
   unsigned int errorCount = 0;
   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
 
-#ifdef MHD_HTTPS_REQUIRE_GCRYPT
-#ifdef HAVE_GCRYPT_H
+#ifdef NEED_GCRYP_INIT
   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
 #ifdef GCRYCTL_INITIALIZATION_FINISHED
   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
-#endif
-#endif
-#endif /* MHD_HTTPS_REQUIRE_GCRYPT */
+#endif /* GCRYCTL_INITIALIZATION_FINISHED */
+#endif /* NEED_GCRYP_INIT */
   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
     return 2;
   errorCount += testDigestAuth ();
diff --git a/src/testcurl/test_digestauth2.c b/src/testcurl/test_digestauth2.c
index 81a93b5e..ab8d2b07 100644
--- a/src/testcurl/test_digestauth2.c
+++ b/src/testcurl/test_digestauth2.c
@@ -25,7 +25,7 @@
  * @author Karlson2k (Evgeny Grin)
  */
 
-#include "MHD_config.h"
+#include "mhd_options.h"
 #include "platform.h"
 #include <curl/curl.h>
 #include <microhttpd.h>
@@ -33,6 +33,12 @@
 #include <string.h>
 #include <time.h>
 
+#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
+  (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
+#define NEED_GCRYP_INIT 1
+#include <gcrypt.h>
+#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
+
 #ifndef _WIN32
 #include <sys/socket.h>
 #include <unistd.h>
@@ -1464,6 +1470,12 @@ main (int argc, char *const *argv)
   int curl_sspi;
   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
 
+#ifdef NEED_GCRYP_INIT
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif /* GCRYCTL_INITIALIZATION_FINISHED */
+#endif /* NEED_GCRYP_INIT */
   /* Test type and test parameters */
   verbose = ! (has_param (argc, argv, "-q") ||
                has_param (argc, argv, "--quiet") ||
diff --git a/src/testcurl/test_digestauth_concurrent.c 
b/src/testcurl/test_digestauth_concurrent.c
index d2bcfd47..792d15de 100644
--- a/src/testcurl/test_digestauth_concurrent.c
+++ b/src/testcurl/test_digestauth_concurrent.c
@@ -26,18 +26,18 @@
  * @author Karlson2k (Evgeny Grin)
  */
 
-#include "MHD_config.h"
+#include "mhd_options.h"
 #include "platform.h"
 #include <curl/curl.h>
 #include <microhttpd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
-#ifdef MHD_HTTPS_REQUIRE_GCRYPT
-#ifdef HAVE_GCRYPT_H
+#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
+  (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
+#define NEED_GCRYP_INIT 1
 #include <gcrypt.h>
-#endif
-#endif /* MHD_HTTPS_REQUIRE_GCRYPT */
+#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
 
 #ifndef WINDOWS
 #include <sys/socket.h>
@@ -669,14 +669,12 @@ main (int argc, char *const *argv)
                has_param (argc, argv, "-s") ||
                has_param (argc, argv, "--silent"));
 
-  #ifdef MHD_HTTPS_REQUIRE_GCRYPT
-#ifdef HAVE_GCRYPT_H
+#ifdef NEED_GCRYP_INIT
   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
 #ifdef GCRYCTL_INITIALIZATION_FINISHED
   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
-#endif
-#endif
-#endif /* MHD_HTTPS_REQUIRE_GCRYPT */
+#endif /* GCRYCTL_INITIALIZATION_FINISHED */
+#endif /* NEED_GCRYP_INIT */
   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
     return 2;
   errorCount += testDigestAuth ();
diff --git a/src/testcurl/test_digestauth_sha256.c 
b/src/testcurl/test_digestauth_sha256.c
index 0781c44c..cf6ab8ff 100644
--- a/src/testcurl/test_digestauth_sha256.c
+++ b/src/testcurl/test_digestauth_sha256.c
@@ -27,18 +27,19 @@
  * @author Karlson2k (Evgeny Grin)
  */
 
-#include "MHD_config.h"
+#include "mhd_options.h"
 #include "platform.h"
 #include <curl/curl.h>
 #include <microhttpd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
-#ifdef MHD_HTTPS_REQUIRE_GCRYPT
-#ifdef HAVE_GCRYPT_H
+
+#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
+  (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
+#define NEED_GCRYP_INIT 1
 #include <gcrypt.h>
-#endif
-#endif /* MHD_HTTPS_REQUIRE_GCRYPT */
+#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
 
 #ifndef WINDOWS
 #include <sys/socket.h>
@@ -320,14 +321,12 @@ main (int argc, char *const *argv)
   /* curl added SHA256 support in 7.57 = 7.0x39 */
   if (d->version_num < 0x073900)
     return 77; /* skip test, curl is too old */
-#ifdef MHD_HTTPS_REQUIRE_GCRYPT
-#ifdef HAVE_GCRYPT_H
+#ifdef NEED_GCRYP_INIT
   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
 #ifdef GCRYCTL_INITIALIZATION_FINISHED
   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
-#endif
-#endif
-#endif /* MHD_HTTPS_REQUIRE_GCRYPT */
+#endif /* GCRYCTL_INITIALIZATION_FINISHED */
+#endif /* NEED_GCRYP_INIT */
   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
     return 2;
   errorCount += testDigestAuth ();
diff --git a/src/testcurl/test_digestauth_with_arguments.c 
b/src/testcurl/test_digestauth_with_arguments.c
index 09d70674..903d90a2 100644
--- a/src/testcurl/test_digestauth_with_arguments.c
+++ b/src/testcurl/test_digestauth_with_arguments.c
@@ -25,18 +25,19 @@
  * @author Amr Ali
  * @author Karlson2k (Evgeny Grin)
  */
-#include "MHD_config.h"
+#include "mhd_options.h"
 #include "platform.h"
 #include <curl/curl.h>
 #include <microhttpd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
-#ifdef MHD_HTTPS_REQUIRE_GCRYPT
-#ifdef HAVE_GCRYPT_H
+
+#if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
+  (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
+#define NEED_GCRYP_INIT 1
 #include <gcrypt.h>
-#endif
-#endif /* MHD_HTTPS_REQUIRE_GCRYPT */
+#endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
 
 #ifndef WINDOWS
 #include <sys/socket.h>
diff --git a/w32/common/MHD_config.h b/w32/common/MHD_config.h
index 7a658d7b..d3ac3f7e 100644
--- a/w32/common/MHD_config.h
+++ b/w32/common/MHD_config.h
@@ -74,6 +74,12 @@
 /* Enable digest Auth support */
 #define DAUTH_SUPPORT 1
 
+/* Enable MD5 hashing support. */
+#define MHD_MD5_SUPPORT 1
+
+/* Enable SHA-256 hashing support. */
+#define MHD_SHA256_SUPPORT 1
+
 /* Enable postprocessor.c */
 #define HAVE_POSTPROCESSOR 1
 
diff --git a/w32/common/libmicrohttpd-files.vcxproj 
b/w32/common/libmicrohttpd-files.vcxproj
index 1818f0df..c78a2c7c 100644
--- a/w32/common/libmicrohttpd-files.vcxproj
+++ b/w32/common/libmicrohttpd-files.vcxproj
@@ -38,7 +38,9 @@
     <ClInclude Include="$(MhdSrc)microhttpd\digestauth.h" />
     <ClInclude Include="$(MhdSrc)microhttpd\gen_auth.h" />
     <ClInclude Include="$(MhdSrc)microhttpd\internal.h" />
+    <ClInclude Include="$(MhdSrc)microhttpd\mhd_md5_wrap.h" />
     <ClInclude Include="$(MhdSrc)microhttpd\md5.h" />
+    <ClInclude Include="$(MhdSrc)microhttpd\mhd_sha256_wrap.h" />
     <ClInclude Include="$(MhdSrc)microhttpd\sha256.h" />
     <ClInclude Include="$(MhdSrc)microhttpd\memorypool.h" />
     <ClInclude Include="$(MhdSrc)microhttpd\mhd_assert.h" />
diff --git a/w32/common/libmicrohttpd-filters.vcxproj 
b/w32/common/libmicrohttpd-filters.vcxproj
index 76c0c56e..346fc9d7 100644
--- a/w32/common/libmicrohttpd-filters.vcxproj
+++ b/w32/common/libmicrohttpd-filters.vcxproj
@@ -58,9 +58,15 @@
     <ClInclude Include="$(MhdSrc)microhttpd\internal.h">
       <Filter>Internal Headers</Filter>
     </ClInclude>
+    <ClInclude Include="$(MhdSrc)microhttpd\mhd_md5_wrap.h">
+      <Filter>Internal Headers</Filter>
+    </ClInclude>
     <ClInclude Include="$(MhdSrc)microhttpd\md5.h">
       <Filter>Internal Headers</Filter>
     </ClInclude>
+    <ClInclude Include="$(MhdSrc)microhttpd\mhd_sha256_wrap.h">
+      <Filter>Internal Headers</Filter>
+    </ClInclude>
     <ClInclude Include="$(MhdSrc)microhttpd\sha256.h">
       <Filter>Internal Headers</Filter>
     </ClInclude>

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



reply via email to

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