commit e154ae81202e274d51d869d4d4834c96dd6668d4
parent 9e8304ce62bc5690b49b4c0552ee18ea79af7a47
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 21 Nov 2025 19:55:05 +0100
implement reading of crypto-quality random generators (TLS backends, /dev/urandom, others) to offload application when digest auth is needed; for now hidden behind #if OLD in auth_digest.c and also still lacking configure.ac check for MBEDTLS
Diffstat:
6 files changed, 452 insertions(+), 7 deletions(-)
diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am
@@ -45,6 +45,7 @@ libmicrohttpd2_la_SOURCES = \
mhd_atomic_counter.c mhd_atomic_counter.h \
mhd_bool.h \
mhd_str.c mhd_str.h \
+ mhd_rng.c mhd_rng.h \
mhd_str_macros.h mhd_str_types.h \
mhd_buffer.h \
mhd_comm_layer_state.h \
@@ -108,7 +109,7 @@ libmicrohttpd2_la_SOURCES += \
compat_calloc.c
endif
-#
+#
httptwo_OPTSOURCES = \
mhd_http_layer_state.h \
h2/h2_bit_masks.h \
@@ -293,7 +294,7 @@ $(srcdir)/response_set_options.c: $(INCL_SRC_DIR)/r_options.rec $(INCL_SRC_DIR)/
AM_V_RC = $(am__v_RC_@AM_V@)
am__v_RC_ = $(am__v_RC_@AM_DEFAULT_V@)
am__v_RC_0 = @echo " RC " $@;
-am__v_RC_1 =
+am__v_RC_1 =
# General rule is not required, but keep it just in case
# Note: windres does not understand '-isystem' flag, so all
@@ -311,7 +312,7 @@ libmicrohttpd2_la-w32_lib_res.lo: $(builddir)/w32_lib_res.rc
if HAVE_W32
MHD_DLL_RES_LO = libmicrohttpd2_la-w32_lib_res.lo
else
-MHD_DLL_RES_LO =
+MHD_DLL_RES_LO =
endif
EXTRA_libmicrohttpd2_la_DEPENDENCIES = $(MHD_DLL_RES_LO)
diff --git a/src/mhd2/auth_digest.c b/src/mhd2/auth_digest.c
@@ -60,6 +60,7 @@
#include "mhd_arr_num_elems.h"
#include "mhd_cntnr_ptr.h"
#include "mhd_limits.h"
+#include "mhd_rng.h"
#include "mhd_str_types.h"
#include "mhd_buffer.h"
@@ -303,20 +304,31 @@ gen_new_nonce (struct MHD_Daemon *restrict d,
uint_fast32_t *restrict expir)
{
uint_fast64_t expiration;
+#define OLD 1
+#if OLD
size_t gen_num;
union DigestCtx d_ctx;
+#endif
mhd_assert (! mhd_D_HAS_MASTER (d)); /* only master daemon should be used */
mhd_assert (d == c->daemon);
mhd_assert (0 != d->auth_dg.cfg.nonce_tmout);
- gen_num = mhd_atomic_counter_get_inc_wrap (&(d->auth_dg.num_gen_nonces));
-
expiration = mhd_monotonic_msec_counter ()
+ d->auth_dg.cfg.nonce_tmout * (uint_fast64_t) 1000;
// TODO: replace with pure random number
+#if ! OLD
+ if (! mhd_rng (d,
+ c,
+ mhd_AUTH_DIGEST_NONCE_BIN_SIZE,
+ out_buf))
+ return false;
+#else
+
+ gen_num = mhd_atomic_counter_get_inc_wrap (&(d->auth_dg.num_gen_nonces));
+
#if defined(MHD_SUPPORT_SHA512_256)
mhd_SHA512_256_init_one_time (&(d_ctx.sha512_256_ctx));
mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
@@ -401,6 +413,8 @@ gen_new_nonce (struct MHD_Daemon *restrict d,
return false;
#endif /* MHD_SUPPORT_MD5 */
+#endif /* old code */
+
*expir = (uint_fast32_t) (expiration / 1000u);
mhd_PUT_32BIT_LE_UNALIGN (out_buf + mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE, \
(uint32_t) (*expir & UINT32_C (0xFFFFFFFF)));
diff --git a/src/mhd2/mhd_recv.h b/src/mhd2/mhd_recv.h
@@ -69,7 +69,7 @@ mhd_recv (struct MHD_Connection *restrict c,
size_t buf_size,
char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
size_t *restrict received)
-MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_(3,2) MHD_FN_PAR_OUT_ (4);
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4);
#endif /* ! MHD_RECV_H */
diff --git a/src/mhd2/mhd_rng.c b/src/mhd2/mhd_rng.c
@@ -0,0 +1,357 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
+/*
+ This file is part of GNU libmicrohttpd.
+ Copyright (C) 2025 Christian Grothoff
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ Alternatively, you can redistribute GNU libmicrohttpd and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version, together
+ with the eCos exception, as follows:
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile this
+ file and link it with other works to produce a work based on this
+ file, this file does not by itself cause the resulting work to be
+ covered by the GNU General Public License. However the source code
+ for this file must still be made available in accordance with
+ section (3) of the GNU General Public License v2.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+
+ You should have received copies of the GNU Lesser General Public
+ License and the GNU General Public License along with this library;
+ if not, see <https://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file src/mhd2/mhd_rng.c
+ * @brief generate random numbers using the best available method;
+ * we begin by trying the TLS libraries, then common operating-system
+ * specific methods, then fall back to /dev/urandom or /dev/random
+ * and if nothing works hash our entropy pool mixing in data from
+ * the context
+ * @author Christian Grothoff
+ */
+
+#include "mhd_sys_options.h"
+#include "mhd_digest_auth_data.h"
+
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+
+#if defined(MHD_SUPPORT_OPENSSL)
+#include <openssl/rand.h>
+#endif
+#if defined(MHD_SUPPORT_GNUTLS)
+#include <gnutls/crypto.h>
+#endif
+#if defined(MHD_SUPPORT_MBEDTLS)
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+#endif
+#if defined(__linux__)
+#include <sys/random.h>
+#include <unistd.h>
+#elif defined(_WIN32) || defined(_WIN64)
+#include <windows.h>
+#include <bcrypt.h>
+#pragma comment(lib, "bcrypt.lib")
+#elif defined(__FreeBSD__)
+#include <sys/random.h>
+#else
+#include <stdio.h>
+#include <unistd.h>
+#endif
+
+#ifdef MHD_SUPPORT_SHA512_256
+# include "mhd_sha512_256.h"
+#endif /* MHD_SUPPORT_SHA512_256 */
+#ifdef MHD_SUPPORT_SHA256
+# include "mhd_sha256.h"
+#endif
+#ifdef MHD_SUPPORT_MD5
+# include "mhd_md5.h"
+#endif
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+#include "mhd_mono_clock.h"
+#include "mhd_atomic_counter.h"
+
+#include "mhd_rng.h"
+
+/**
+ * Digest context data
+ */
+union DigestCtx
+{
+#ifdef MHD_SUPPORT_SHA512_256
+ struct mhd_Sha512_256Ctx sha512_256_ctx;
+#endif /* MHD_SUPPORT_SHA512_256 */
+#ifdef MHD_SUPPORT_SHA256
+ struct mhd_Sha256Ctx sha256_ctx;
+#endif /* MHD_SUPPORT_SHA256 */
+#ifdef MHD_SUPPORT_MD5
+ struct mhd_Md5Ctx md5_ctx;
+#endif /* MHD_SUPPORT_MD5 */
+};
+
+
+MHD_INTERNAL bool
+mhd_rng (struct MHD_Daemon *restrict d,
+ struct MHD_Connection *restrict c,
+ size_t buf_size,
+ uint8_t buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)])
+{
+ #if defined(MHD_SUPPORT_OPENSSL)
+ /* OpenSSL - RAND_bytes() */
+ if (1 == RAND_bytes ((unsigned char *) buf,
+ buf_size))
+ return true;
+#endif
+#if defined(MHD_SUPPORT_GNUTLS)
+ /* GnuTLS - gnutls_rnd() */
+ if (0 ==
+ gnutls_rnd (GNUTLS_RND_RANDOM, buf,
+ buf_size))
+ return true;
+#endif
+#if defined(MHD_SUPPORT_MBEDTLS)
+ {
+ /* mbedTLS - requires entropy context and DRBG */
+ static mbedtls_entropy_context entropy;
+ static mbedtls_ctr_drbg_context ctr_drbg;
+ static int initialized = 0;
+
+ if (0 == initialized)
+ {
+ mbedtls_entropy_init (&entropy);
+ mbedtls_ctr_drbg_init (&ctr_drbg);
+
+ if (0 !=
+ mbedtls_ctr_drbg_seed (&ctr_drbg,
+ mbedtls_entropy_func,
+ &entropy,
+ NULL,
+ 0))
+ {
+ initialized = -1;
+ }
+ else
+ {
+ initialized = 1;
+ }
+ }
+
+ if ( (1 == initialized) &&
+ (0 ==
+ mbedtls_ctr_drbg_random (&ctr_drbg,
+ (unsigned char *) buf,
+ buf_size)) )
+ return true;
+ }
+#endif
+#if defined(__linux__) && defined(__GLIBC__) && \
+ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))
+ {
+ /* Modern Linux with glibc >= 2.25 - getrandom() syscall */
+ size_t offset = 0;
+ unsigned char *ptr = (unsigned char *) buf;
+
+ while (1)
+ {
+ ssize_t ret;
+
+ ret = getrandom (ptr + offset,
+ buf_size - offset,
+ 0);
+ if (ret < 0)
+ {
+ if (EINTR == errno)
+ {
+ continue;
+ }
+ break; /* failure */
+ }
+ offset += ret;
+ if (offset == buf_size)
+ return true;
+ }
+ }
+#elif defined(_WIN32) || defined(_WIN64)
+ /* Windows - BCryptGenRandom() */
+ if (STATUS_SUCCESS ==
+ BCryptGenRandom (NULL,
+ (PUCHAR) buf,
+ (ULONG) buf_size,
+ BCRYPT_USE_SYSTEM_PREFERRED_RNG))
+ return true;
+#elif defined(__FreeBSD__)
+ /* FreeBSD - arc4random_buf() */
+ arc4random_buf (buf,
+ buf_size);
+ return true;
+#else
+ /* Generic UNIX fallback - read from /dev/urandom */
+ {
+ static int tried = 0;
+ static FILE *fp = NULL;
+
+ if ( (0 == tried) &&
+ (NULL == fp) )
+ {
+ tried = 1;
+ fp = fopen ("/dev/urandom",
+ "rb");
+ if (NULL == fp)
+ {
+ /* Try /dev/random as last resort */
+ fp = fopen ("/dev/random",
+ "rb");
+ }
+ }
+ if (NULL != fp)
+ {
+ size_t bytes_read;
+
+ bytes_read = fread (buf,
+ 1,
+ buf_size,
+ fp);
+ if (bytes_read == buf_size)
+ return true;
+ }
+ }
+#endif
+ /* Nothing worked, use hash functions and our entropy pool and
+ combine it with current time, generation counter and (if
+ available) client address to make the inputs as unique as
+ possible. */
+ {
+ size_t off = 0;
+
+ while (off < buf_size)
+ {
+ size_t gen_num;
+ union DigestCtx d_ctx;
+ uint_fast64_t now;
+ /* size of the output buffer, same for all of our hash functions */
+ uint8_t out_buf[mhd_AUTH_DIGEST_NONCE_BIN_SIZE];
+ size_t left;
+
+ gen_num = mhd_atomic_counter_get_inc_wrap (&(d->auth_dg.num_gen_nonces));
+ now = mhd_monotonic_msec_counter ();
+
+#if defined(MHD_SUPPORT_SHA512_256)
+ mhd_SHA512_256_init_one_time (&(d_ctx.sha512_256_ctx));
+ mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+ d->auth_dg.entropy.size,
+ (const uint8_t*) d->auth_dg.entropy.data);
+ mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+ sizeof(gen_num),
+ (const uint8_t*) &gen_num);
+ if ( (NULL != c) &&
+ (0 != c->sk.addr.size) )
+ mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+ c->sk.addr.size,
+ (const uint8_t*) c->sk.addr.data);
+ mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx),
+ sizeof(now),
+ (const uint8_t*) &now);
+ mhd_SHA512_256_finish_deinit (&(d_ctx.sha512_256_ctx), \
+ out_buf);
+ if (mhd_SHA512_256_has_err (&(d_ctx.sha512_256_ctx)))
+ return false;
+#elif defined(MHD_SUPPORT_SHA256)
+ mhd_SHA256_init_one_time (&(d_ctx.sha256_ctx));
+ mhd_SHA256_update (&(d_ctx.sha256_ctx),
+ d->auth_dg.entropy.size,
+ (const void*) d->auth_dg.entropy.data);
+ mhd_SHA256_update (&(d_ctx.sha256_ctx),
+ sizeof(gen_num),
+ (const void*) &gen_num);
+ if ( (NULL != c) &&
+ (0 != c->sk.addr.size) )
+ mhd_SHA256_update (&(d_ctx.sha256_ctx),
+ c->sk.addr.size,
+ (const void*) c->sk.addr.data);
+ mhd_SHA256_update (&(d_ctx.sha256_ctx),
+ sizeof(now),
+ (const void*) &now);
+ mhd_SHA256_finish_deinit (&(d_ctx.sha256_ctx), \
+ out_buf);
+ if (mhd_SHA256_has_err (&(d_ctx.sha256_ctx)))
+ return false;
+#else /* MHD_SUPPORT_MD5 */
+#ifndef MHD_SUPPORT_MD5
+#error At least one hashing algorithm must be enabled
+#endif
+ mhd_MD5_init_one_time (&(d_ctx.md5_ctx));
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ d->auth_dg.entropy.size,
+ (const void*) d->auth_dg.entropy.data);
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ sizeof(gen_num),
+ (const void*) &gen_num);
+ if ( (NULL != c) &&
+ (0 != c->sk.addr.size) )
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ c->sk.addr.size,
+ (const void*) c->sk.addr.data);
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ sizeof(now),
+ (const void*) &now);
+ mhd_MD5_finish_deinit (&(d_ctx.md5_ctx), \
+ out_buf);
+ if (mhd_MD5_has_err (&(d_ctx.md5_ctx)))
+ return false;
+
+ /* One more hash, for the second part */
+ gen_num = mhd_atomic_counter_get_inc_wrap (&(d->auth_dg.num_gen_nonces));
+
+ mhd_MD5_init_one_time (&(d_ctx.md5_ctx));
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ d->auth_dg.entropy.size,
+ (const void*) d->auth_dg.entropy.data);
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ sizeof(gen_num),
+ (const void*) &gen_num);
+ if ( (NULL != c) &&
+ (0 != c->sk.addr.size) )
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ c->sk.addr.size,
+ (const void*) c->sk.addr.data);
+ mhd_MD5_update (&(d_ctx.md5_ctx),
+ sizeof(now),
+ (const void*) &now);
+ mhd_MD5_finish_deinit (&(d_ctx.md5_ctx), \
+ out_buf + mhd_MD5_DIGEST_SIZE);
+ if (mhd_MD5_has_err (&(d_ctx.md5_ctx)))
+ return false;
+#endif /* MHD_SUPPORT_MD5 */
+
+ left = mhd_AUTH_DIGEST_NONCE_BIN_SIZE < (buf_size - off)
+ ? mhd_AUTH_DIGEST_NONCE_BIN_SIZE
+ : (buf_size - off);
+ memcpy (buf + off,
+ out_buf,
+ left);
+ off += left;
+ } /* end while off < buf_size */
+ } /* end hash-based approach scope */
+ return true;
+}
diff --git a/src/mhd2/mhd_rng.h b/src/mhd2/mhd_rng.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
+/*
+ This file is part of GNU libmicrohttpd.
+ Copyright (C) 2025 Christian Grothoff
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU libmicrohttpd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ Alternatively, you can redistribute GNU libmicrohttpd and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version, together
+ with the eCos exception, as follows:
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile this
+ file and link it with other works to produce a work based on this
+ file, this file does not by itself cause the resulting work to be
+ covered by the GNU General Public License. However the source code
+ for this file must still be made available in accordance with
+ section (3) of the GNU General Public License v2.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+
+ You should have received copies of the GNU Lesser General Public
+ License and the GNU General Public License along with this library;
+ if not, see <https://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file src/mhd2/mhd_rng.h
+ * @brief The definition of the mhd_rng() function
+ * @author Christian Grothoff
+ */
+
+#ifndef MHD_RECV_H
+#define MHD_RECV_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+#include "mhd_socket_error.h"
+
+struct MHD_Daemon; /* forward declaration */
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Initialize @a buf with random data.
+ *
+ * @param d the daemon to use
+ * @param c client to create entropy for, can be NULL
+ * @param buf_size the size of the @a buf buffer
+ * @param[out] buf the buffer to fill with the random bytes
+ * @return true on success
+ */
+MHD_INTERNAL bool
+mhd_rng (struct MHD_Daemon *restrict d,
+ struct MHD_Connection *restrict c,
+ size_t buf_size,
+ uint8_t buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)])
+MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_OUT_SIZE_ (4,3);
+
+#endif
diff --git a/src/tests/client_server/test_http2.c b/src/tests/client_server/test_http2.c
@@ -178,7 +178,6 @@ main (int argc, char *argv[])
.timeout_ms = 2500,
.http_version = 2,
},
-#if 0
{
.label = "GET with sendfile",
.server_cb = &MHDT_server_reply_file,
@@ -188,6 +187,7 @@ main (int argc, char *argv[])
.timeout_ms = 2500,
.http_version = 2,
},
+#if 0
{
.label = "client PUT with content-length",
.server_cb = &MHDT_server_reply_check_upload,