libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

commit 81e09d29ede48bc9443bd8e86c9d090ec74991e0
parent 5228ab0e225ee97e07f79bd950df24adba5780ac
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date:   Sat, 11 Jul 2026 21:18:56 +0200

Reworked MD5 backends handling, fixed number of problems

Removed two levels dispatcher/selector, simplified to single level.
Optimised by avoiding unneeded context resets and using proper internal
API functions.

Diffstat:
Msrc/mhd2/Makefile.am | 14+++++++-------
Msrc/mhd2/auth_digest.c | 21+++++----------------
Asrc/mhd2/md5_builtin.c | 563+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/md5_builtin.h | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/mhd2/md5_ext.h | 131-------------------------------------------------------------------------------
Dsrc/mhd2/md5_ext_gnutls.c | 129-------------------------------------------------------------------------------
Dsrc/mhd2/md5_ext_mbedtls.c | 139-------------------------------------------------------------------------------
Dsrc/mhd2/md5_ext_openssl.c | 153-------------------------------------------------------------------------------
Asrc/mhd2/md5_gnutls.c | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/md5_gnutls.h | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/mhd2/md5_int.c | 557-------------------------------------------------------------------------------
Dsrc/mhd2/md5_int.h | 147-------------------------------------------------------------------------------
Asrc/mhd2/md5_mbedtls.c | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/md5_mbedtls.h | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/md5_openssl.c | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/md5_openssl.h | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/mhd_macro_concat.h | 10++++++++++
Msrc/mhd2/mhd_md5.h | 247++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/tests/unit/Makefile.am | 19++++++++++++++++++-
Msrc/tests/unit/unit_md5.c | 99+++++++++++++++++++++++++++++++++++++++++--------------------------------------
20 files changed, 1882 insertions(+), 1380 deletions(-)

diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am @@ -161,26 +161,26 @@ auth_basic_OPTSOURCES = \ if MHD_USE_MD5_INTR md5_OPTSOURCES = \ - md5_int.c md5_int.h \ + md5_builtin.c md5_builtin.h \ mhd_md5.h endif if MHD_USE_MD5_GNUTLS md5_OPTSOURCES = \ - md5_ext.h mhd_md5.h \ - md5_ext_gnutls.c + md5_gnutls.c md5_gnutls.h \ + mhd_md5.h endif if MHD_USE_MD5_OPENSSL md5_OPTSOURCES = \ - md5_ext.h mhd_md5.h \ - md5_ext_openssl.c + md5_openssl.c md5_openssl.h \ + mhd_md5.h endif if MHD_USE_MD5_MBEDTLS md5_OPTSOURCES = \ - md5_ext.h mhd_md5.h \ - md5_ext_mbedtls.c + md5_mbedtls.c md5_mbedtls.h \ + mhd_md5.h endif diff --git a/src/mhd2/auth_digest.c b/src/mhd2/auth_digest.c @@ -366,7 +366,7 @@ gen_new_nonce (struct MHD_Daemon *restrict d, # 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_init (&(d_ctx.md5_ctx)); mhd_MD5_update (&(d_ctx.md5_ctx), d->auth_dg.entropy.size, (const void *)d->auth_dg.entropy.data); @@ -388,7 +388,7 @@ gen_new_nonce (struct MHD_Daemon *restrict d, /* 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_init (&(d_ctx.md5_ctx)); mhd_MD5_update (&(d_ctx.md5_ctx), d->auth_dg.entropy.size, (const void *)d->auth_dg.entropy.data); @@ -1593,7 +1593,7 @@ digest_init_one_time (struct DigestAlgorithm *da, # ifndef NDEBUG da->algo_selected = true; # endif - mhd_MD5_init_one_time (&(da->ctx.md5_ctx)); + mhd_MD5_init (&(da->ctx.md5_ctx)); # ifndef NDEBUG da->ready_for_hashing = true; # endif @@ -1774,17 +1774,10 @@ digest_calc_hash (struct DigestAlgorithm *da, { case MHD_DIGEST_BASE_ALGO_MD5: #ifdef MHD_SUPPORT_MD5 -# ifdef mhd_MD5_HAS_FINISH mhd_MD5_finish (&da->ctx.md5_ctx, digest); -# ifndef NDEBUG +# ifndef NDEBUG da->ready_for_hashing = false; -# endif /* _DEBUG */ -# else /* ! mhd_MD5_HAS_FINISH */ - mhd_MD5_finish_reset (&da->ctx.md5_ctx, digest); -# ifndef NDEBUG - da->ready_for_hashing = true; -# endif /* _DEBUG */ -# endif /* ! mhd_MD5_HAS_FINISH */ +# endif /* _DEBUG */ #else /* ! MHD_SUPPORT_MD5 */ mhd_UNREACHABLE (); #endif /* ! MHD_SUPPORT_MD5 */ @@ -1852,11 +1845,7 @@ digest_reset (struct DigestAlgorithm *da) { case MHD_DIGEST_BASE_ALGO_MD5: #ifdef MHD_SUPPORT_MD5 -# ifdef mhd_MD5_HAS_FINISH mhd_assert (!da->ready_for_hashing); -# else /* ! mhd_MD5_HAS_FINISH */ - mhd_assert (da->ready_for_hashing); -# endif /* ! mhd_MD5_HAS_FINISH */ mhd_MD5_reset (&(da->ctx.md5_ctx)); # ifndef NDEBUG da->ready_for_hashing = true; diff --git a/src/mhd2/md5_builtin.c b/src/mhd2/md5_builtin.c @@ -0,0 +1,563 @@ +/* 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) 2022-2026 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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/md5_builtin.c + * @brief Calculation of MD5 digest as defined in RFC 1321 + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +#include <string.h> +#include "mhd_bithelpers.h" +#include "mhd_align.h" +#include "mhd_assert.h" + +#include "md5_builtin.h" + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (1) void +mhd_MD5_blti_init (struct mhd_Md5CtxBlti *ctx) +{ + /* Initial hash values, see RFC 1321, Clause 3.3 (step 3). */ + /* Note: values specified in RFC by bytes and should be loaded in + little-endian mode, therefore hash values here are initialised with + original bytes used in little-endian order. */ + ctx->H[0] = UINT32_C (0x67452301); + ctx->H[1] = UINT32_C (0xefcdab89); + ctx->H[2] = UINT32_C (0x98badcfe); + ctx->H[3] = UINT32_C (0x10325476); + + /* Initialise the number of bytes. */ + ctx->count = 0; +} + + +mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE + +/** + * Base of MD5 transformation. + * Gets full 64 bytes block of data and updates hash values; + * @param H hash values + * @param M the data buffer with #mhd_MD5_BLOCK_SIZE bytes block + */ +static MHD_FN_PAR_NONNULL_ALL_ void +md5_transform (uint32_t H[mhd_MD5_HASH_SIZE_WORDS], + const void *restrict M) +{ + /* Working variables, + See RFC 1321, Clause 3.4 (step 4). */ + uint32_t A = H[0]; + uint32_t B = H[1]; + uint32_t C = H[2]; + uint32_t D = H[3]; + + /* The data buffer. See RFC 1321, Clause 3.4 (step 4). */ + uint32_t X[16]; + +#ifndef mhd_GET_32BIT_LE_UNALIGNED + if (0 != (((uintptr_t)M) % mhd_UINT32_ALIGN)) + { /* The input data is unaligned. */ + /* Copy the unaligned input data to the aligned buffer. */ + memcpy (X, M, sizeof(X)); + /* The X[] buffer itself will be used as the source of the data, + * but the data will be reloaded in correct bytes order on + * the next steps. */ + M = (const void *)X; + } +#endif /* mhd_GET_32BIT_LE_UNALIGNED */ + + /* Four auxiliary functions, see RFC 1321, Clause 3.4 (step 4). */ + /* Some optimisations used. */ +/* #define F_FUNC(x,y,z) (((x)&(y)) | ((~(x))&(z))) */ /* Original version */ +#define F_FUNC(x, y, z) ((((y) ^ (z)) & (x)) ^ (z)) +/* #define G_FUNC_1(x,y,z) (((x)&(z)) | ((y)&(~(z)))) */ /* Original version */ +/* #define G_FUNC_2(x,y,z) UINT32_C(0) */ /* Original version */ +#ifndef MHD_FAVOR_SMALL_CODE +# define G_FUNC_1(x, y, z) ((~(z)) & (y)) +# define G_FUNC_2(x, y, z) ((z) & (x)) +#else /* MHD_FAVOR_SMALL_CODE */ +# define G_FUNC_1(x, y, z) ((((x) ^ (y)) & (z)) ^ (y)) +# define G_FUNC_2(x, y, z) UINT32_C (0) +#endif /* MHD_FAVOR_SMALL_CODE */ +#define H_FUNC(x, y, z) ((x) ^ (y) ^ (z)) /* Original version */ +/* #define I_FUNC(x,y,z) ((y) ^ ((x) | (~(z)))) */ /* Original version */ +#define I_FUNC(x, y, z) (((~(z)) | (x)) ^ (y)) + + /* One step of round 1 of MD5 computation, see RFC 1321, Clause 3.4 (step 4). + The original function was modified to use X[k] and T[i] as + direct inputs. */ +#define MD5STEP_R1(va, vb, vc, vd, vX, vs, vT) do { \ + (va) += (vX) + (vT); \ + (va) += F_FUNC ((vb),(vc),(vd)); \ + (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0) + + /* Get value of X(k) from input data buffer. + See RFC 1321 Clause 3.4 (step 4). */ +#define GET_X_FROM_DATA(buf, t) \ + mhd_GET_32BIT_LE (((const uint32_t*) (buf)) + (t)) + + /* One step of round 2 of MD5 computation, see RFC 1321, Clause 3.4 (step 4). + The original function was modified to use X[k] and T[i] as + direct inputs. */ +#define MD5STEP_R2(va, vb, vc, vd, vX, vs, vT) do { \ + (va) += (vX) + (vT); \ + (va) += G_FUNC_1 ((vb),(vc),(vd)); \ + (va) += G_FUNC_2 ((vb),(vc),(vd)); \ + (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0) + + /* One step of round 3 of MD5 computation, see RFC 1321, Clause 3.4 (step 4). + The original function was modified to use X[k] and T[i] as + direct inputs. */ +#define MD5STEP_R3(va, vb, vc, vd, vX, vs, vT) do { \ + (va) += (vX) + (vT); \ + (va) += H_FUNC ((vb),(vc),(vd)); \ + (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0) + + /* One step of round 4 of MD5 computation, see RFC 1321, Clause 3.4 (step 4). + The original function was modified to use X[k] and T[i] as + direct inputs. */ +#define MD5STEP_R4(va, vb, vc, vd, vX, vs, vT) do { \ + (va) += (vX) + (vT); \ + (va) += I_FUNC ((vb),(vc),(vd)); \ + (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0) + +#if !defined(MHD_FAVOR_SMALL_CODE) + + /* Round 1. */ + +# if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN + if ((const void *)X == M) + { + /* The input data is already in the data buffer X[] in correct bytes + order. */ + MD5STEP_R1 (A, B, C, D, X[0], 7, UINT32_C (0xd76aa478)); + MD5STEP_R1 (D, A, B, C, X[1], 12, UINT32_C (0xe8c7b756)); + MD5STEP_R1 (C, D, A, B, X[2], 17, UINT32_C (0x242070db)); + MD5STEP_R1 (B, C, D, A, X[3], 22, UINT32_C (0xc1bdceee)); + + MD5STEP_R1 (A, B, C, D, X[4], 7, UINT32_C (0xf57c0faf)); + MD5STEP_R1 (D, A, B, C, X[5], 12, UINT32_C (0x4787c62a)); + MD5STEP_R1 (C, D, A, B, X[6], 17, UINT32_C (0xa8304613)); + MD5STEP_R1 (B, C, D, A, X[7], 22, UINT32_C (0xfd469501)); + + MD5STEP_R1 (A, B, C, D, X[8], 7, UINT32_C (0x698098d8)); + MD5STEP_R1 (D, A, B, C, X[9], 12, UINT32_C (0x8b44f7af)); + MD5STEP_R1 (C, D, A, B, X[10], 17, UINT32_C (0xffff5bb1)); + MD5STEP_R1 (B, C, D, A, X[11], 22, UINT32_C (0x895cd7be)); + + MD5STEP_R1 (A, B, C, D, X[12], 7, UINT32_C (0x6b901122)); + MD5STEP_R1 (D, A, B, C, X[13], 12, UINT32_C (0xfd987193)); + MD5STEP_R1 (C, D, A, B, X[14], 17, UINT32_C (0xa679438e)); + MD5STEP_R1 (B, C, D, A, X[15], 22, UINT32_C (0x49b40821)); + } + else /* Combined with the next 'if' */ +# endif /* mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN */ + if (1) + { + /* The input data is loaded in correct (little-endian) format before + calculations on each step. */ + MD5STEP_R1 (A, B, C, D, X[0] = GET_X_FROM_DATA (M, 0), 7, \ + UINT32_C (0xd76aa478)); + MD5STEP_R1 (D, A, B, C, X[1] = GET_X_FROM_DATA (M, 1), 12, \ + UINT32_C (0xe8c7b756)); + MD5STEP_R1 (C, D, A, B, X[2] = GET_X_FROM_DATA (M, 2), 17, \ + UINT32_C (0x242070db)); + MD5STEP_R1 (B, C, D, A, X[3] = GET_X_FROM_DATA (M, 3), 22, \ + UINT32_C (0xc1bdceee)); + + MD5STEP_R1 (A, B, C, D, X[4] = GET_X_FROM_DATA (M, 4), 7, \ + UINT32_C (0xf57c0faf)); + MD5STEP_R1 (D, A, B, C, X[5] = GET_X_FROM_DATA (M, 5), 12, \ + UINT32_C (0x4787c62a)); + MD5STEP_R1 (C, D, A, B, X[6] = GET_X_FROM_DATA (M, 6), 17, \ + UINT32_C (0xa8304613)); + MD5STEP_R1 (B, C, D, A, X[7] = GET_X_FROM_DATA (M, 7), 22, \ + UINT32_C (0xfd469501)); + + MD5STEP_R1 (A, B, C, D, X[8] = GET_X_FROM_DATA (M, 8), 7, \ + UINT32_C (0x698098d8)); + MD5STEP_R1 (D, A, B, C, X[9] = GET_X_FROM_DATA (M, 9), 12, \ + UINT32_C (0x8b44f7af)); + MD5STEP_R1 (C, D, A, B, X[10] = GET_X_FROM_DATA (M, 10), 17, \ + UINT32_C (0xffff5bb1)); + MD5STEP_R1 (B, C, D, A, X[11] = GET_X_FROM_DATA (M, 11), 22, \ + UINT32_C (0x895cd7be)); + + MD5STEP_R1 (A, B, C, D, X[12] = GET_X_FROM_DATA (M, 12), 7, \ + UINT32_C (0x6b901122)); + MD5STEP_R1 (D, A, B, C, X[13] = GET_X_FROM_DATA (M, 13), 12, \ + UINT32_C (0xfd987193)); + MD5STEP_R1 (C, D, A, B, X[14] = GET_X_FROM_DATA (M, 14), 17, \ + UINT32_C (0xa679438e)); + MD5STEP_R1 (B, C, D, A, X[15] = GET_X_FROM_DATA (M, 15), 22, \ + UINT32_C (0x49b40821)); + } + + /* Round 2. */ + + MD5STEP_R2 (A, B, C, D, X[1], 5, UINT32_C (0xf61e2562)); + MD5STEP_R2 (D, A, B, C, X[6], 9, UINT32_C (0xc040b340)); + MD5STEP_R2 (C, D, A, B, X[11], 14, UINT32_C (0x265e5a51)); + MD5STEP_R2 (B, C, D, A, X[0], 20, UINT32_C (0xe9b6c7aa)); + + MD5STEP_R2 (A, B, C, D, X[5], 5, UINT32_C (0xd62f105d)); + MD5STEP_R2 (D, A, B, C, X[10], 9, UINT32_C (0x02441453)); + MD5STEP_R2 (C, D, A, B, X[15], 14, UINT32_C (0xd8a1e681)); + MD5STEP_R2 (B, C, D, A, X[4], 20, UINT32_C (0xe7d3fbc8)); + + MD5STEP_R2 (A, B, C, D, X[9], 5, UINT32_C (0x21e1cde6)); + MD5STEP_R2 (D, A, B, C, X[14], 9, UINT32_C (0xc33707d6)); + MD5STEP_R2 (C, D, A, B, X[3], 14, UINT32_C (0xf4d50d87)); + MD5STEP_R2 (B, C, D, A, X[8], 20, UINT32_C (0x455a14ed)); + + MD5STEP_R2 (A, B, C, D, X[13], 5, UINT32_C (0xa9e3e905)); + MD5STEP_R2 (D, A, B, C, X[2], 9, UINT32_C (0xfcefa3f8)); + MD5STEP_R2 (C, D, A, B, X[7], 14, UINT32_C (0x676f02d9)); + MD5STEP_R2 (B, C, D, A, X[12], 20, UINT32_C (0x8d2a4c8a)); + + /* Round 3. */ + + MD5STEP_R3 (A, B, C, D, X[5], 4, UINT32_C (0xfffa3942)); + MD5STEP_R3 (D, A, B, C, X[8], 11, UINT32_C (0x8771f681)); + MD5STEP_R3 (C, D, A, B, X[11], 16, UINT32_C (0x6d9d6122)); + MD5STEP_R3 (B, C, D, A, X[14], 23, UINT32_C (0xfde5380c)); + + MD5STEP_R3 (A, B, C, D, X[1], 4, UINT32_C (0xa4beea44)); + MD5STEP_R3 (D, A, B, C, X[4], 11, UINT32_C (0x4bdecfa9)); + MD5STEP_R3 (C, D, A, B, X[7], 16, UINT32_C (0xf6bb4b60)); + MD5STEP_R3 (B, C, D, A, X[10], 23, UINT32_C (0xbebfbc70)); + + MD5STEP_R3 (A, B, C, D, X[13], 4, UINT32_C (0x289b7ec6)); + MD5STEP_R3 (D, A, B, C, X[0], 11, UINT32_C (0xeaa127fa)); + MD5STEP_R3 (C, D, A, B, X[3], 16, UINT32_C (0xd4ef3085)); + MD5STEP_R3 (B, C, D, A, X[6], 23, UINT32_C (0x04881d05)); + + MD5STEP_R3 (A, B, C, D, X[9], 4, UINT32_C (0xd9d4d039)); + MD5STEP_R3 (D, A, B, C, X[12], 11, UINT32_C (0xe6db99e5)); + MD5STEP_R3 (C, D, A, B, X[15], 16, UINT32_C (0x1fa27cf8)); + MD5STEP_R3 (B, C, D, A, X[2], 23, UINT32_C (0xc4ac5665)); + + /* Round 4. */ + + MD5STEP_R4 (A, B, C, D, X[0], 6, UINT32_C (0xf4292244)); + MD5STEP_R4 (D, A, B, C, X[7], 10, UINT32_C (0x432aff97)); + MD5STEP_R4 (C, D, A, B, X[14], 15, UINT32_C (0xab9423a7)); + MD5STEP_R4 (B, C, D, A, X[5], 21, UINT32_C (0xfc93a039)); + + MD5STEP_R4 (A, B, C, D, X[12], 6, UINT32_C (0x655b59c3)); + MD5STEP_R4 (D, A, B, C, X[3], 10, UINT32_C (0x8f0ccc92)); + MD5STEP_R4 (C, D, A, B, X[10], 15, UINT32_C (0xffeff47d)); + MD5STEP_R4 (B, C, D, A, X[1], 21, UINT32_C (0x85845dd1)); + + MD5STEP_R4 (A, B, C, D, X[8], 6, UINT32_C (0x6fa87e4f)); + MD5STEP_R4 (D, A, B, C, X[15], 10, UINT32_C (0xfe2ce6e0)); + MD5STEP_R4 (C, D, A, B, X[6], 15, UINT32_C (0xa3014314)); + MD5STEP_R4 (B, C, D, A, X[13], 21, UINT32_C (0x4e0811a1)); + + MD5STEP_R4 (A, B, C, D, X[4], 6, UINT32_C (0xf7537e82)); + MD5STEP_R4 (D, A, B, C, X[11], 10, UINT32_C (0xbd3af235)); + MD5STEP_R4 (C, D, A, B, X[2], 15, UINT32_C (0x2ad7d2bb)); + MD5STEP_R4 (B, C, D, A, X[9], 21, UINT32_C (0xeb86d391)); +#else /* MHD_FAVOR_SMALL_CODE */ + if (1) + { + static const uint32_t T[64] = + { UINT32_C (0xd76aa478), UINT32_C (0xe8c7b756), UINT32_C (0x242070db), + UINT32_C (0xc1bdceee), UINT32_C (0xf57c0faf), UINT32_C (0x4787c62a), + UINT32_C (0xa8304613), UINT32_C (0xfd469501), UINT32_C (0x698098d8), + UINT32_C (0x8b44f7af), UINT32_C (0xffff5bb1), UINT32_C (0x895cd7be), + UINT32_C (0x6b901122), UINT32_C (0xfd987193), UINT32_C (0xa679438e), + UINT32_C (0x49b40821), UINT32_C (0xf61e2562), UINT32_C (0xc040b340), + UINT32_C (0x265e5a51), UINT32_C (0xe9b6c7aa), UINT32_C (0xd62f105d), + UINT32_C (0x02441453), UINT32_C (0xd8a1e681), UINT32_C (0xe7d3fbc8), + UINT32_C (0x21e1cde6), UINT32_C (0xc33707d6), UINT32_C (0xf4d50d87), + UINT32_C (0x455a14ed), UINT32_C (0xa9e3e905), UINT32_C (0xfcefa3f8), + UINT32_C (0x676f02d9), UINT32_C (0x8d2a4c8a), UINT32_C (0xfffa3942), + UINT32_C (0x8771f681), UINT32_C (0x6d9d6122), UINT32_C (0xfde5380c), + UINT32_C (0xa4beea44), UINT32_C (0x4bdecfa9), UINT32_C (0xf6bb4b60), + UINT32_C (0xbebfbc70), UINT32_C (0x289b7ec6), UINT32_C (0xeaa127fa), + UINT32_C (0xd4ef3085), UINT32_C (0x04881d05), UINT32_C (0xd9d4d039), + UINT32_C (0xe6db99e5), UINT32_C (0x1fa27cf8), UINT32_C (0xc4ac5665), + UINT32_C (0xf4292244), UINT32_C (0x432aff97), UINT32_C (0xab9423a7), + UINT32_C (0xfc93a039), UINT32_C (0x655b59c3), UINT32_C (0x8f0ccc92), + UINT32_C (0xffeff47d), UINT32_C (0x85845dd1), UINT32_C (0x6fa87e4f), + UINT32_C (0xfe2ce6e0), UINT32_C (0xa3014314), UINT32_C (0x4e0811a1), + UINT32_C (0xf7537e82), UINT32_C (0xbd3af235), UINT32_C (0x2ad7d2bb), + UINT32_C (0xeb86d391) }; + unsigned int i; /**< Zero-based index */ + + /* Round 1. */ + + i = 0; + do + { + /* The input data is loaded in correct (little-endian) format before + calculations on each step. */ + MD5STEP_R1 (A, B, C, D, X[i] = GET_X_FROM_DATA (M, i), 7, T[i]); + ++i; + MD5STEP_R1 (D, A, B, C, X[i] = GET_X_FROM_DATA (M, i), 12, T[i]); + ++i; + MD5STEP_R1 (C, D, A, B, X[i] = GET_X_FROM_DATA (M, i), 17, T[i]); + ++i; + MD5STEP_R1 (B, C, D, A, X[i] = GET_X_FROM_DATA (M, i), 22, T[i]); + ++i; + } while (i < 16); + + /* Round 2. */ + + do + { + const unsigned int idx_add = i; + MD5STEP_R2 (A, B, C, D, X[(1U + idx_add) & 15U], 5, T[i]); + ++i; + MD5STEP_R2 (D, A, B, C, X[(6U + idx_add) & 15U], 9, T[i]); + ++i; + MD5STEP_R2 (C, D, A, B, X[(11U + idx_add) & 15U], 14, T[i]); + ++i; + MD5STEP_R2 (B, C, D, A, X[(0U + idx_add) & 15U], 20, T[i]); + ++i; + } while (i < 32); + + /* Round 3. */ + + do + { + const unsigned int idx_add = i; + MD5STEP_R3 (A, B, C, D, X[(5U + 64U - idx_add) & 15U], 4, T[i]); + ++i; + MD5STEP_R3 (D, A, B, C, X[(8U + 64U - idx_add) & 15U], 11, T[i]); + ++i; + MD5STEP_R3 (C, D, A, B, X[(11U + 64U - idx_add) & 15U], 16, T[i]); + ++i; + MD5STEP_R3 (B, C, D, A, X[(14U + 64U - idx_add) & 15U], 23, T[i]); + ++i; + } while (i < 48); + + /* Round 4. */ + + do + { + const unsigned int idx_add = i; + MD5STEP_R4 (A, B, C, D, X[(0U + 64U - idx_add) & 15U], 6, T[i]); + ++i; + MD5STEP_R4 (D, A, B, C, X[(7U + 64U - idx_add) & 15U], 10, T[i]); + ++i; + MD5STEP_R4 (C, D, A, B, X[(14U + 64U - idx_add) & 15U], 15, T[i]); + ++i; + MD5STEP_R4 (B, C, D, A, X[(5U + 64U - idx_add) & 15U], 21, T[i]); + ++i; + } while (i < 64); + } +#endif /* MHD_FAVOR_SMALL_CODE */ + + /* Finally increment and store working variables. + See RFC 1321, end of Clause 3.4 (step 4). */ + + H[0] += A; + H[1] += B; + H[2] += C; + H[3] += D; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) +MHD_FN_PAR_IN_SIZE_ (3, 2) void +mhd_MD5_blti_update (struct mhd_Md5CtxBlti *restrict ctx, + size_t size, + const void *restrict data) +{ + unsigned int bytes_have; /**< Number of bytes in the context buffer */ + const uint8_t *unpr_d; + + unpr_d = (const uint8_t *)data; + +#ifndef MHD_UNIT_TESTING + mhd_assert (0 != size); +#endif + + /* Note: (count & (mhd_MD5_BLOCK_SIZE-1)) + equals (count % mhd_MD5_BLOCK_SIZE) for this block size. */ + bytes_have = (unsigned int)(ctx->count & (mhd_MD5_BLOCK_SIZE - 1)); + ctx->count += size; + + if (0 != bytes_have) + { + unsigned int bytes_left = mhd_MD5_BLOCK_SIZE - bytes_have; + if (size >= bytes_left) + { /* Combine new data with data in the buffer and + process the full block. */ + memcpy (((uint8_t *)ctx->buffer) + bytes_have, + unpr_d, + bytes_left); + unpr_d += bytes_left; + size -= bytes_left; + md5_transform (ctx->H, ctx->buffer); + bytes_have = 0; + } + } + + while (mhd_MD5_BLOCK_SIZE <= size) + { /* Process any full blocks of new data directly, + without copying to the buffer. */ + md5_transform (ctx->H, unpr_d); + unpr_d += mhd_MD5_BLOCK_SIZE; + size -= mhd_MD5_BLOCK_SIZE; + } + + if (0 != size) + { /* Copy incomplete block of new data (if any) + to the buffer. */ + memcpy (((uint8_t *)ctx->buffer) + bytes_have, unpr_d, size); + } +} + + +/** + * Size of "length" insertion in bits. + * See RFC 1321, end of Clause 3.2 (step 2). + */ +#define MD5_SIZE_OF_LEN_ADD_BITS 64 + +/** + * Size of "length" insertion in bytes. + */ +#define MD5_SIZE_OF_LEN_ADD (MD5_SIZE_OF_LEN_ADD_BITS / 8) + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) +MHD_FN_PAR_OUT_ (2) void +mhd_MD5_blti_finish ( + struct mhd_Md5CtxBlti *restrict ctx, + uint8_t digest[MHD_FN_PAR_FIX_ARR_SIZE_ (mhd_MD5_DIGEST_SIZE)]) +{ + uint64_t num_bits; /**< Number of processed bits */ + unsigned int bytes_have; /**< Number of bytes in the context buffer */ + + /* Memorise the number of processed bits. + The padding and other data added here during the postprocessing must + not change the amount of hashed data. */ + num_bits = ctx->count << 3; + + /* Note: (count & (mhd_MD5_BLOCK_SIZE-1)) + equals (count % mhd_MD5_BLOCK_SIZE) for this block size. */ + bytes_have = (unsigned int)(ctx->count & (mhd_MD5_BLOCK_SIZE - 1)); + + /* Input data must be padded with a single bit "1", then with zeros and + the finally the length of data in bits must be added as the final bytes + of the last block. + See RFC 1321, Clauses 3.1 and 3.2 (steps 1 and 2). */ + /* Data is always processed in form of bytes (not by individual bits), + therefore position of the first padding bit in byte is always + predefined (0x80). */ + /* Buffer always have space for one byte at least (as full buffers are + processed immediately). */ + ((uint8_t *)ctx->buffer)[bytes_have++] = 0x80; + + if (mhd_MD5_BLOCK_SIZE - bytes_have < MD5_SIZE_OF_LEN_ADD) + { /* No space in the current block to put the total length of message. + Pad the current block with zeros and process it. */ + if (bytes_have < mhd_MD5_BLOCK_SIZE) + memset (((uint8_t *)ctx->buffer) + bytes_have, 0, + mhd_MD5_BLOCK_SIZE - bytes_have); + /* Process the full block. */ + md5_transform (ctx->H, ctx->buffer); + /* Start the new block. */ + bytes_have = 0; + } + + /* Pad the rest of the buffer with zeros. */ + memset (((uint8_t *)ctx->buffer) + bytes_have, 0, + mhd_MD5_BLOCK_SIZE - MD5_SIZE_OF_LEN_ADD - bytes_have); + /* Put the number of bits in processed data as little-endian value. + See RFC 1321, clauses 2 and 3.2 (step 2). */ + mhd_PUT_64BIT_LE_UNALIGN (ctx->buffer + mhd_MD5_BLOCK_SIZE_WORDS - 2, + num_bits); + /* Process the full final block. */ + md5_transform (ctx->H, ctx->buffer); + + /* Put in LE mode the hash as the final digest. + See RFC 1321, clauses 2 and 3.5 (step 5). */ + if (1) + { + bool use_tmp_buf_to_align_result; + +#if defined(mhd_PUT_32BIT_LE_UNALIGNED) + use_tmp_buf_to_align_result = false; +#elif defined(MHD_FAVOR_SMALL_CODE) + use_tmp_buf_to_align_result = true; /* smaller code: eliminated branch below */ +#else + use_tmp_buf_to_align_result = + (0 != ((uintptr_t)digest) % mhd_UINT32_ALIGN); +#endif + if (use_tmp_buf_to_align_result) + { + /* If storing of the final result requires aligned address and + the destination address is not aligned or compact code is used, + store the final digest in aligned temporary buffer first, then + copy it to the destination. */ + uint32_t alig_dgst[mhd_MD5_DIGEST_SIZE_WORDS]; + mhd_PUT_32BIT_LE (alig_dgst + 0, ctx->H[0]); + mhd_PUT_32BIT_LE (alig_dgst + 1, ctx->H[1]); + mhd_PUT_32BIT_LE (alig_dgst + 2, ctx->H[2]); + mhd_PUT_32BIT_LE (alig_dgst + 3, ctx->H[3]); + /* Copy result to the unaligned destination address. */ + memcpy (digest, alig_dgst, mhd_MD5_DIGEST_SIZE); + } + else + { + /* Use cast to (void*) here to mute compiler alignment warnings. + * Compilers are not smart enough to see that alignment has been checked. */ + mhd_PUT_32BIT_LE ((void *)(digest + 0 * mhd_MD5_BYTES_IN_WORD), \ + ctx->H[0]); + mhd_PUT_32BIT_LE ((void *)(digest + 1 * mhd_MD5_BYTES_IN_WORD), \ + ctx->H[1]); + mhd_PUT_32BIT_LE ((void *)(digest + 2 * mhd_MD5_BYTES_IN_WORD), \ + ctx->H[2]); + mhd_PUT_32BIT_LE ((void *)(digest + 3 * mhd_MD5_BYTES_IN_WORD), \ + ctx->H[3]); + } + } + + /* Erase potentially sensitive data. */ + memset (ctx, 0, sizeof(struct mhd_Md5CtxBlti)); +} + + +mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE diff --git a/src/mhd2/md5_builtin.h b/src/mhd2/md5_builtin.h @@ -0,0 +1,182 @@ +/* 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) 2022-2026 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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/md5_builtin.h + * @brief Calculation of MD5 digest, built-in implementation + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_MD5_BUILTIN_H +#define MHD_MD5_BUILTIN_H 1 + +#include "mhd_sys_options.h" + +#include "sys_base_types.h" + +/** + * Number of bits in single MD5 word. + */ +#define mhd_MD5_WORD_SIZE_BITS (32) + +/** + * Number of bytes in single MD5 word. + */ +#define mhd_MD5_BYTES_IN_WORD (mhd_MD5_WORD_SIZE_BITS / 8) + +/** + * Hash is kept internally as four 32-bit words. + * This is the intermediate hash size, used while computing the final digest. + */ +#define mhd_MD5_HASH_SIZE_WORDS (4) + +/** + * Size of MD5 resulting digest in bytes. + * This is the final digest size, not the intermediate hash. + */ +#define mhd_MD5_DIGEST_SIZE_WORDS mhd_MD5_HASH_SIZE_WORDS + +#ifndef mhd_MD5_DIGEST_SIZE +/** + * Size of MD5 resulting digest in bytes + * This is the final digest size, not the intermediate hash. + */ +# define mhd_MD5_DIGEST_SIZE \ + (mhd_MD5_DIGEST_SIZE_WORDS * mhd_MD5_BYTES_IN_WORD) +#endif + +/** + * Size of MD5 single processing block in bits. + */ +#define mhd_MD5_BLOCK_SIZE_BITS 512 + +/** + * Size of MD5 single processing block in bytes. + */ +#define mhd_MD5_BLOCK_SIZE (mhd_MD5_BLOCK_SIZE_BITS / 8) + +/** + * Size of MD5 single processing block in words. + */ +#define mhd_MD5_BLOCK_SIZE_WORDS \ + (mhd_MD5_BLOCK_SIZE_BITS / mhd_MD5_WORD_SIZE_BITS) + + +/** + * MD5 calculation context + */ +struct mhd_Md5CtxBlti +{ + uint32_t H[mhd_MD5_HASH_SIZE_WORDS]; /**< Intermediate hash value / digest at end of calculation */ + uint32_t buffer[mhd_MD5_BLOCK_SIZE_WORDS]; /**< MD5 input data buffer */ + uint64_t count; /**< number of bytes, mod 2^64 */ +}; + +/** + * Indicates whether struct mhd_Md5CtxBlti has an 'ext_error' member. + * + * Set to '0' when the structure has no 'ext_error' member. + */ +#define mhd_MD5_BLTI_EXT_ERROR_FLAG 0 + +/** + * Initialise the context for MD5 calculation. + * + * This function must not be called more than once for @a ctx without an + * intervening #mhd_MD5_blti_deinit(). + * + * @param ctx the calculation context + */ +MHD_INTERNAL void +mhd_MD5_blti_init (struct mhd_Md5CtxBlti *ctx) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (1); + +/** + * Add a portion of data to the MD5 calculation. + * + * @param ctx the calculation context + * @param size number of bytes in @a data, must not be 0 + * @param data bytes to add to hash + */ +MHD_INTERNAL void +mhd_MD5_blti_update (struct mhd_Md5CtxBlti *restrict ctx, + size_t size, + const void *restrict data) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_IN_SIZE_ (3, 2); + +/** + * Finalise the MD5 calculation and return the digest. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes + */ +MHD_INTERNAL void +mhd_MD5_blti_finish ( + struct mhd_Md5CtxBlti *restrict ctx, + uint8_t digest[MHD_FN_PAR_FIX_ARR_SIZE_ (mhd_MD5_DIGEST_SIZE)]) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_OUT_ (2); + +/** + * Reset the context for a new MD5 calculation. + * + * This function may be called only after #mhd_MD5_blti_finish(). If the context + * is subsequently reused for a new calculation, this function must be called + * before starting that calculation. + * + * @param ctx the calculation context + */ +#define mhd_MD5_blti_reset(ctx) mhd_MD5_blti_init((ctx)) + +/** + * Deinitialise the MD5 calculation context. + * + * After calling this function, @a ctx can be reinitialised with + * #mhd_MD5_blti_init(). + * + * @param ctx the calculation context + */ +#define mhd_MD5_blti_deinit(ctx) ((void) (ctx)) + +/** + * Indicates whether #mhd_MD5_blti_deinit() is a real function or a no-op. + * + * Set to '1' as #mhd_MD5_blti_deinit() is a no-op. + */ +#define mhd_MD5_BLTI_DEINIT_NOOP_FLAG 1 + +#endif /* MHD_MD5_BUILTIN_H */ diff --git a/src/mhd2/md5_ext.h b/src/mhd2/md5_ext.h @@ -1,131 +0,0 @@ -/* 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) 2022-2024 Evgeny Grin (Karlson2k) - - GNU libmicrohttpd is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - GNU libmicrohttpd is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - 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/md5_ext.h - * @brief Wrapper declarations for MD5 calculation performed by TLS library - * @author Karlson2k (Evgeny Grin) - */ -#ifndef MHD_MD5_EXT_H -#define MHD_MD5_EXT_H 1 - -#include "mhd_sys_options.h" - -#include "sys_base_types.h" - -/** - * Size of MD5 resulting digest in bytes - * This is the final digest size, not intermediate hash. - */ -#define mhd_MD5_DIGEST_SIZE (16) - -#ifndef MHD_MD5_Context -# define MHD_MD5_Context void -#endif - -/** - * Indicates that struct mhd_Md5CtxExt has 'ext_error' - */ -#define mhd_MD5_HAS_EXT_ERROR 1 - -/** - * MD5 calculation context - */ -struct mhd_Md5CtxExt -{ - MHD_MD5_Context *handle; /**< Hash calculation handle */ - int ext_error; /**< Non-zero if external error occurs during init or hashing */ -}; - -/** - * Indicates that mhd_MD5_init_one_time() function is present. - */ -#define mhd_MD5_HAS_INIT_ONE_TIME 1 - -/** - * Initialise structure for MD5 calculation, allocate resources. - * - * This function must not be called more than one time for @a ctx. - * - * @param ctx the calculation context - */ -void -mhd_MD5_init_one_time (struct mhd_Md5CtxExt *ctx); - - -/** - * MD5 process portion of bytes. - * - * @param ctx the calculation context - * @param size number of bytes in @a data, must not be 0 - * @param data bytes to add to hash - */ -void -mhd_MD5_update (struct mhd_Md5CtxExt *restrict ctx, - size_t size, - const uint8_t *restrict data); - - -/** - * Indicates that mhd_MD5_finish_reset() function is available - */ -#define mhd_MD5_HAS_FINISH_RESET 1 - -/** - * Finalise MD5 calculation, return digest, reset hash calculation. - * - * @param ctx the calculation context - * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes - */ -void -mhd_MD5_finish_reset (struct mhd_Md5CtxExt *restrict ctx, - uint8_t digest[mhd_MD5_DIGEST_SIZE]); - -/** - * Indicates that mhd_MD5_deinit() function is present - */ -#define mhd_MD5_HAS_DEINIT 1 - -/** - * Free allocated resources. - * - * @param[in] ctx the calculation context - */ -void -mhd_MD5_deinit (struct mhd_Md5CtxExt *ctx); - -#endif /* MHD_MD5_EXT_H */ diff --git a/src/mhd2/md5_ext_gnutls.c b/src/mhd2/md5_ext_gnutls.c @@ -1,129 +0,0 @@ -/* 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) 2022-2023 Evgeny Grin (Karlson2k) - - GNU libmicrohttpd is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - 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 microhttpd/md5_ext.c - * @brief Wrapper for MD5 calculation performed by TLS library - * @author Karlson2k (Evgeny Grin) - */ - -#include <gnutls/crypto.h> -#define MHD_MD5_Context struct hash_hd_st -#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 mhd_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 may return initialisation error and set the handle at the - same time. Such handle cannot be used for calculations. - Note: GnuTLS may also return an error and NOT set the handle. */ - mhd_MD5_deinit (ctx); - } - - /* If handle is NULL, the error must be set */ - mhd_assert ((NULL != ctx->handle) || (0 != ctx->ext_error)); - /* If error is set, the handle must be NULL */ - mhd_assert ((0 == ctx->ext_error) || (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 mhd_Md5CtxExt *ctx, - size_t size, - const uint8_t *data) -{ - mhd_assert (0 != size); - if (0 == ctx->ext_error) - ctx->ext_error = gnutls_hash (ctx->handle, - data, - size); -} - - -/** - * Finalise MD5 calculation, return digest, reset hash calculation. - * - * @param ctx the calculation context - * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes - */ -void -mhd_MD5_finish_reset (struct mhd_Md5CtxExt *ctx, - uint8_t digest[mhd_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 mhd_Md5CtxExt *ctx) -{ - if (NULL != ctx->handle) - { - gnutls_hash_deinit (ctx->handle, - NULL); - ctx->handle = NULL; - } -} diff --git a/src/mhd2/md5_ext_mbedtls.c b/src/mhd2/md5_ext_mbedtls.c @@ -1,139 +0,0 @@ -/* 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 microhttpd/md5_ext_mbedtls.c - * @brief Wrapper for MD5 calculation performed by mbedTLS library - * @author Christian Grothoff - */ - -#include <mbedtls/md5.h> -#define MHD_MD5_Context mbedtls_md5_context -#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 mhd_Md5CtxExt *ctx) -{ - ctx->ext_error = 0; - ctx->handle = (mbedtls_md5_context *)malloc ( - sizeof (mbedtls_md5_context)); - if (NULL == ctx->handle) - { - ctx->ext_error = 1; /* Allocation failure */ - return; - } - mbedtls_md5_init (ctx->handle); - ctx->ext_error = mbedtls_md5_starts_ret (ctx->handle); - if (0 != ctx->ext_error) - { - mhd_MD5_deinit (ctx->handle); - } - - /* If handle is NULL, the error must be set */ - mhd_assert ((NULL != ctx->handle) || (0 != ctx->ext_error)); - /* If error is set, the handle must be NULL */ - mhd_assert ((0 == ctx->ext_error) || (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 mhd_Md5CtxExt *ctx, - size_t size, - const uint8_t *data) -{ - mhd_assert (0 != size); - - if (0 == ctx->ext_error) - ctx->ext_error = mbedtls_md5_update_ret (ctx->handle, - data, - size); -} - - -/** - * Finalise MD5 calculation, return digest, reset hash calculation. - * - * @param ctx the calculation context - * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes - */ -void -mhd_MD5_finish_reset (struct mhd_Md5CtxExt *ctx, - uint8_t digest[mhd_MD5_DIGEST_SIZE]) -{ - if (0 != ctx->ext_error) - return; - ctx->ext_error = mbedtls_md5_finish_ret (ctx->handle, - digest); - if (0 != ctx->ext_error) - return; - /* Reset for potential reuse */ - ctx->ext_error = mbedtls_md5_starts_ret (ctx->handle); -} - - -/** - * Free allocated resources. - * - * @param ctx the calculation context - */ -void -mhd_MD5_deinit (struct mhd_Md5CtxExt *ctx) -{ - if (NULL != ctx->handle) - { - mbedtls_md5_free (ctx->handle); - free (ctx->handle); - ctx->handle = NULL; - } -} diff --git a/src/mhd2/md5_ext_openssl.c b/src/mhd2/md5_ext_openssl.c @@ -1,153 +0,0 @@ -/* 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 microhttpd/md5_ext_openssl.c - * @brief Wrapper for MD5 calculation performed by OpenSSL library - * @author Christian grothoff - */ - -#include <openssl/evp.h> -#define MHD_MD5_Context EVP_MD_CTX -#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 mhd_Md5CtxExt *ctx) -{ - ctx->ext_error = 0; - ctx->handle = EVP_MD_CTX_new (); - if (NULL == ctx->handle) - { - ctx->ext_error = 1; /* Allocation failure */ - return; - } - if (1 != EVP_DigestInit_ex (ctx->handle, - EVP_md5 (), - NULL)) - { - ctx->ext_error = 1; /* Initialization failure */ - mhd_MD5_deinit (ctx); - } - - /* If handle is NULL, the error must be set */ - mhd_assert ((NULL != ctx->handle) || (0 != ctx->ext_error)); - /* If error is set, the handle must be NULL */ - mhd_assert ((0 == ctx->ext_error) || (NULL == ctx->handle)); -} - - -/** - * Process portion of bytes. - * - * @param ctx the calculation context - * @param size number of bytes in @a data, must not be 0 - * @param data bytes to add to hash - */ -void -mhd_MD5_update (struct mhd_Md5CtxExt *ctx, - size_t size, - const uint8_t *data) -{ - mhd_assert (0 != size); - - if (0 == ctx->ext_error) - { - if (1 != EVP_DigestUpdate (ctx->handle, - data, - size)) - ctx->ext_error = 1; - } -} - - -/** - * Finalise MD5 calculation, return digest, reset hash calculation. - * - * @param ctx the calculation context - * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes - */ -void -mhd_MD5_finish_reset (struct mhd_Md5CtxExt *ctx, - uint8_t digest[mhd_MD5_DIGEST_SIZE]) -{ - unsigned int len; - - if (0 != ctx->ext_error) - return; - if (1 != EVP_DigestFinal_ex (ctx->handle, - digest, - &len)) - { - ctx->ext_error = 1; - return; - } - mhd_assert (mhd_MD5_DIGEST_SIZE == len); - /* Reset for potential reuse */ - if (1 != EVP_DigestInit_ex (ctx->handle, - EVP_md5 (), - NULL)) - { - ctx->ext_error = 1; - mhd_MD5_deinit (ctx); - } -} - - -/** - * Free allocated resources. - * - * @param[in] ctx the calculation context - */ -void -mhd_MD5_deinit (struct mhd_Md5CtxExt *ctx) -{ - if (NULL != ctx->handle) - { - EVP_MD_CTX_free (ctx->handle); - ctx->handle = NULL; - } -} diff --git a/src/mhd2/md5_gnutls.c b/src/mhd2/md5_gnutls.c @@ -0,0 +1,113 @@ +/* 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) 2022-2026 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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/md5_gnutls.c + * @brief Wrapper for MD5 calculation performed by GnuTLS backend + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include <gnutls/crypto.h> +#include "mhd_assert.h" + +#include "md5_gnutls.h" + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (1) void +mhd_MD5_gtls_init (struct mhd_Md5CtxGtls *ctx) +{ + ctx->handle = NULL; + ctx->ext_error = gnutls_hash_init (&ctx->handle, + GNUTLS_DIG_MD5); + if ((0 != ctx->ext_error) && (NULL != ctx->handle)) + { + /* GnuTLS may return an initialisation error and set the handle at the + same time. Such a handle cannot be used for calculations. + Note: GnuTLS may also return an error and NOT set the handle. */ + mhd_MD5_gtls_deinit (ctx); + } + + /* If handle is NULL, the error must be set */ + mhd_assert ((NULL != ctx->handle) || (0 != ctx->ext_error)); + /* If error is set, the handle must be NULL */ + mhd_assert ((0 == ctx->ext_error) || (NULL == ctx->handle)); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) +MHD_FN_PAR_IN_SIZE_ (3, 2) void +mhd_MD5_gtls_update (struct mhd_Md5CtxGtls *restrict ctx, + size_t size, + const void *restrict data) +{ +#ifndef MHD_UNIT_TESTING + mhd_assert (0 != size); +#endif + + if (0 == ctx->ext_error) + ctx->ext_error = gnutls_hash (ctx->handle, + data, + size); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) +MHD_FN_PAR_OUT_ (2) void +mhd_MD5_gtls_finish ( + struct mhd_Md5CtxGtls *restrict ctx, + uint8_t digest[MHD_FN_PAR_FIX_ARR_SIZE_ (mhd_MD5_DIGEST_SIZE)]) +{ + if (0 == ctx->ext_error) + gnutls_hash_output (ctx->handle, + digest); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_INOUT_ (1) void +mhd_MD5_gtls_deinit (struct mhd_Md5CtxGtls *ctx) +{ + if (NULL != ctx->handle) + { + gnutls_hash_deinit (ctx->handle, + NULL); + ctx->handle = NULL; + } +} diff --git a/src/mhd2/md5_gnutls.h b/src/mhd2/md5_gnutls.h @@ -0,0 +1,164 @@ +/* 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) 2022-2026 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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/md5_gnutls.h + * @brief Wrapper declarations for MD5 calculation performed by GnuTLS backend + * @author Karlson2k (Evgeny Grin) + */ +#ifndef MHD_MD5_GNUTLS_H +#define MHD_MD5_GNUTLS_H 1 + +#include "mhd_sys_options.h" + +#include "sys_base_types.h" + +#ifndef mhd_MD5_DIGEST_SIZE +/** + * Size of MD5 resulting digest in bytes + * This is the final digest size, not the intermediate hash. + */ +# define mhd_MD5_DIGEST_SIZE (16) +#endif + +/* Forward declaration for GnuTLS struct, to avoid including large header */ +struct hash_hd_st; + +/** + * MD5 calculation context + */ +struct mhd_Md5CtxGtls +{ + struct hash_hd_st *handle; /**< Hash calculation handle */ + int ext_error; /**< Error number reported by the hashing backend (zero if no error) */ +}; + +/** + * Indicates whether struct mhd_Md5CtxGtls has an 'ext_error' member. + * + * Set to '1' when the structure has an 'ext_error' member. + */ +#define mhd_MD5_GTLS_EXT_ERROR_FLAG 1 + +/** + * Initialise the context for MD5 calculation. + * + * This function must not be called more than once for @a ctx without an + * intervening #mhd_MD5_gtls_deinit(). + * + * Whether or not an error is reported, #mhd_MD5_gtls_deinit() must be + * called for @a ctx once this function has returned. + * + * The error state in @a ctx is set according to the result: cleared on + * success, set on failure. + * + * @param ctx the calculation context + */ +MHD_INTERNAL void +mhd_MD5_gtls_init (struct mhd_Md5CtxGtls *ctx) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (1); + +/** + * Add a portion of data to the MD5 calculation. + * + * If @a ctx already has a recorded error, this function does nothing. + * Otherwise, if the operation fails, the error is recorded in @a ctx. + * + * @param ctx the calculation context + * @param size number of bytes in @a data, must not be 0 + * @param data bytes to add to hash + */ +MHD_INTERNAL void +mhd_MD5_gtls_update (struct mhd_Md5CtxGtls *restrict ctx, + size_t size, + const void *restrict data) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_IN_SIZE_ (3, 2); + +/** + * Finalise the MD5 calculation and return the digest. + * + * If @a ctx already has a recorded error, this function does nothing. + * + * The GnuTLS backend resets the underlying hash handle during this operation. + * If the context is reused, the caller must still invoke #mhd_MD5_gtls_reset() + * afterwards to follow the common MD5 context lifecycle; that reset operation + * is a no-op. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes + */ +MHD_INTERNAL void +mhd_MD5_gtls_finish ( + struct mhd_Md5CtxGtls *restrict ctx, + uint8_t digest[MHD_FN_PAR_FIX_ARR_SIZE_ (mhd_MD5_DIGEST_SIZE)]) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_OUT_ (2); + +/** + * Reset the context for a new MD5 calculation. + * + * This function may be called only after #mhd_MD5_gtls_finish(). If the context + * is subsequently reused for a new calculation, this function must be called + * before starting that calculation. + * + * This operation is a no-op; the recorded error state in @a ctx is preserved. + * + * @param ctx the calculation context + */ +#define mhd_MD5_gtls_reset(ctx) ((void) (ctx)) + +/** + * Deinitialise the MD5 calculation context. + * + * After calling this function, @a ctx can be reinitialised with + * #mhd_MD5_gtls_init(). + * + * The recorded error state in @a ctx is preserved. + * + * @param ctx the calculation context + */ +MHD_INTERNAL void +mhd_MD5_gtls_deinit (struct mhd_Md5CtxGtls *ctx) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); + +/** + * Indicates whether #mhd_MD5_gtls_deinit() is a real function or a no-op. + * + * Set to '0' as #mhd_MD5_gtls_deinit() is a real function. + */ +#define mhd_MD5_GTLS_DEINIT_NOOP_FLAG 0 + +#endif /* MHD_MD5_GNUTLS_H */ diff --git a/src/mhd2/md5_int.c b/src/mhd2/md5_int.c @@ -1,557 +0,0 @@ -/* 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) 2022-2024 Evgeny Grin (Karlson2k) - - GNU libmicrohttpd is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - GNU libmicrohttpd is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - 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/md5_int.c - * @brief Calculation of MD5 digest as defined in RFC 1321 - * @author Karlson2k (Evgeny Grin) - */ - -#include "mhd_sys_options.h" - -#include "sys_bool_type.h" - -#include <string.h> -#include "mhd_bithelpers.h" -#include "mhd_align.h" -#include "mhd_assert.h" - -#include "md5_int.h" - -MHD_INTERNAL void MHD_FN_PAR_NONNULL_ALL_ -mhd_MD5_init (struct mhd_Md5CtxInt *ctx) -{ - /* Initial hash values, see RFC 1321, Clause 3.3 (step 3). */ - /* Note: values specified in RFC by bytes and should be loaded in - little-endian mode, therefore hash values here are initialised with - original bytes used in little-endian order. */ - ctx->H[0] = UINT32_C (0x67452301); - ctx->H[1] = UINT32_C (0xefcdab89); - ctx->H[2] = UINT32_C (0x98badcfe); - ctx->H[3] = UINT32_C (0x10325476); - - /* Initialise the number of bytes. */ - ctx->count = 0; -} - - -mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE - -/** - * Base of MD5 transformation. - * Gets full 64 bytes block of data and updates hash values; - * @param H hash values - * @param M the data buffer with #mhd_MD5_BLOCK_SIZE bytes block - */ -static MHD_FN_PAR_NONNULL_ALL_ void -md5_transform (uint32_t H[mhd_MD5_HASH_SIZE_WORDS], - const void *restrict M) -{ - /* Working variables, - See RFC 1321, Clause 3.4 (step 4). */ - uint32_t A = H[0]; - uint32_t B = H[1]; - uint32_t C = H[2]; - uint32_t D = H[3]; - - /* The data buffer. See RFC 1321, Clause 3.4 (step 4). */ - uint32_t X[16]; - -#ifndef mhd_GET_32BIT_LE_UNALIGNED - if (0 != (((uintptr_t)M) % mhd_UINT32_ALIGN)) - { /* The input data is unaligned. */ - /* Copy the unaligned input data to the aligned buffer. */ - memcpy (X, M, sizeof(X)); - /* The X[] buffer itself will be used as the source of the data, - * but the data will be reloaded in correct bytes order on - * the next steps. */ - M = (const void *)X; - } -#endif /* mhd_GET_32BIT_LE_UNALIGNED */ - - /* Four auxiliary functions, see RFC 1321, Clause 3.4 (step 4). */ - /* Some optimisations used. */ -/* #define F_FUNC(x,y,z) (((x)&(y)) | ((~(x))&(z))) */ /* Original version */ -#define F_FUNC(x, y, z) ((((y) ^ (z)) & (x)) ^ (z)) -/* #define G_FUNC_1(x,y,z) (((x)&(z)) | ((y)&(~(z)))) */ /* Original version */ -/* #define G_FUNC_2(x,y,z) UINT32_C(0) */ /* Original version */ -#ifndef MHD_FAVOR_SMALL_CODE -# define G_FUNC_1(x, y, z) ((~(z)) & (y)) -# define G_FUNC_2(x, y, z) ((z) & (x)) -#else /* MHD_FAVOR_SMALL_CODE */ -# define G_FUNC_1(x, y, z) ((((x) ^ (y)) & (z)) ^ (y)) -# define G_FUNC_2(x, y, z) UINT32_C (0) -#endif /* MHD_FAVOR_SMALL_CODE */ -#define H_FUNC(x, y, z) ((x) ^ (y) ^ (z)) /* Original version */ -/* #define I_FUNC(x,y,z) ((y) ^ ((x) | (~(z)))) */ /* Original version */ -#define I_FUNC(x, y, z) (((~(z)) | (x)) ^ (y)) - - /* One step of round 1 of MD5 computation, see RFC 1321, Clause 3.4 (step 4). - The original function was modified to use X[k] and T[i] as - direct inputs. */ -#define MD5STEP_R1(va, vb, vc, vd, vX, vs, vT) do { \ - (va) += (vX) + (vT); \ - (va) += F_FUNC ((vb),(vc),(vd)); \ - (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0) - - /* Get value of X(k) from input data buffer. - See RFC 1321 Clause 3.4 (step 4). */ -#define GET_X_FROM_DATA(buf, t) \ - mhd_GET_32BIT_LE (((const uint32_t*) (buf)) + (t)) - - /* One step of round 2 of MD5 computation, see RFC 1321, Clause 3.4 (step 4). - The original function was modified to use X[k] and T[i] as - direct inputs. */ -#define MD5STEP_R2(va, vb, vc, vd, vX, vs, vT) do { \ - (va) += (vX) + (vT); \ - (va) += G_FUNC_1 ((vb),(vc),(vd)); \ - (va) += G_FUNC_2 ((vb),(vc),(vd)); \ - (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0) - - /* One step of round 3 of MD5 computation, see RFC 1321, Clause 3.4 (step 4). - The original function was modified to use X[k] and T[i] as - direct inputs. */ -#define MD5STEP_R3(va, vb, vc, vd, vX, vs, vT) do { \ - (va) += (vX) + (vT); \ - (va) += H_FUNC ((vb),(vc),(vd)); \ - (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0) - - /* One step of round 4 of MD5 computation, see RFC 1321, Clause 3.4 (step 4). - The original function was modified to use X[k] and T[i] as - direct inputs. */ -#define MD5STEP_R4(va, vb, vc, vd, vX, vs, vT) do { \ - (va) += (vX) + (vT); \ - (va) += I_FUNC ((vb),(vc),(vd)); \ - (va) = mhd_ROTL32 ((va),(vs)) + (vb); } while (0) - -#if !defined(MHD_FAVOR_SMALL_CODE) - - /* Round 1. */ - -# if mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN - if ((const void *)X == M) - { - /* The input data is already in the data buffer X[] in correct bytes - order. */ - MD5STEP_R1 (A, B, C, D, X[0], 7, UINT32_C (0xd76aa478)); - MD5STEP_R1 (D, A, B, C, X[1], 12, UINT32_C (0xe8c7b756)); - MD5STEP_R1 (C, D, A, B, X[2], 17, UINT32_C (0x242070db)); - MD5STEP_R1 (B, C, D, A, X[3], 22, UINT32_C (0xc1bdceee)); - - MD5STEP_R1 (A, B, C, D, X[4], 7, UINT32_C (0xf57c0faf)); - MD5STEP_R1 (D, A, B, C, X[5], 12, UINT32_C (0x4787c62a)); - MD5STEP_R1 (C, D, A, B, X[6], 17, UINT32_C (0xa8304613)); - MD5STEP_R1 (B, C, D, A, X[7], 22, UINT32_C (0xfd469501)); - - MD5STEP_R1 (A, B, C, D, X[8], 7, UINT32_C (0x698098d8)); - MD5STEP_R1 (D, A, B, C, X[9], 12, UINT32_C (0x8b44f7af)); - MD5STEP_R1 (C, D, A, B, X[10], 17, UINT32_C (0xffff5bb1)); - MD5STEP_R1 (B, C, D, A, X[11], 22, UINT32_C (0x895cd7be)); - - MD5STEP_R1 (A, B, C, D, X[12], 7, UINT32_C (0x6b901122)); - MD5STEP_R1 (D, A, B, C, X[13], 12, UINT32_C (0xfd987193)); - MD5STEP_R1 (C, D, A, B, X[14], 17, UINT32_C (0xa679438e)); - MD5STEP_R1 (B, C, D, A, X[15], 22, UINT32_C (0x49b40821)); - } - else /* Combined with the next 'if' */ -# endif /* mhd_BYTE_ORDER == mhd_LITTLE_ENDIAN */ - if (1) - { - /* The input data is loaded in correct (little-endian) format before - calculations on each step. */ - MD5STEP_R1 (A, B, C, D, X[0] = GET_X_FROM_DATA (M, 0), 7, \ - UINT32_C (0xd76aa478)); - MD5STEP_R1 (D, A, B, C, X[1] = GET_X_FROM_DATA (M, 1), 12, \ - UINT32_C (0xe8c7b756)); - MD5STEP_R1 (C, D, A, B, X[2] = GET_X_FROM_DATA (M, 2), 17, \ - UINT32_C (0x242070db)); - MD5STEP_R1 (B, C, D, A, X[3] = GET_X_FROM_DATA (M, 3), 22, \ - UINT32_C (0xc1bdceee)); - - MD5STEP_R1 (A, B, C, D, X[4] = GET_X_FROM_DATA (M, 4), 7, \ - UINT32_C (0xf57c0faf)); - MD5STEP_R1 (D, A, B, C, X[5] = GET_X_FROM_DATA (M, 5), 12, \ - UINT32_C (0x4787c62a)); - MD5STEP_R1 (C, D, A, B, X[6] = GET_X_FROM_DATA (M, 6), 17, \ - UINT32_C (0xa8304613)); - MD5STEP_R1 (B, C, D, A, X[7] = GET_X_FROM_DATA (M, 7), 22, \ - UINT32_C (0xfd469501)); - - MD5STEP_R1 (A, B, C, D, X[8] = GET_X_FROM_DATA (M, 8), 7, \ - UINT32_C (0x698098d8)); - MD5STEP_R1 (D, A, B, C, X[9] = GET_X_FROM_DATA (M, 9), 12, \ - UINT32_C (0x8b44f7af)); - MD5STEP_R1 (C, D, A, B, X[10] = GET_X_FROM_DATA (M, 10), 17, \ - UINT32_C (0xffff5bb1)); - MD5STEP_R1 (B, C, D, A, X[11] = GET_X_FROM_DATA (M, 11), 22, \ - UINT32_C (0x895cd7be)); - - MD5STEP_R1 (A, B, C, D, X[12] = GET_X_FROM_DATA (M, 12), 7, \ - UINT32_C (0x6b901122)); - MD5STEP_R1 (D, A, B, C, X[13] = GET_X_FROM_DATA (M, 13), 12, \ - UINT32_C (0xfd987193)); - MD5STEP_R1 (C, D, A, B, X[14] = GET_X_FROM_DATA (M, 14), 17, \ - UINT32_C (0xa679438e)); - MD5STEP_R1 (B, C, D, A, X[15] = GET_X_FROM_DATA (M, 15), 22, \ - UINT32_C (0x49b40821)); - } - - /* Round 2. */ - - MD5STEP_R2 (A, B, C, D, X[1], 5, UINT32_C (0xf61e2562)); - MD5STEP_R2 (D, A, B, C, X[6], 9, UINT32_C (0xc040b340)); - MD5STEP_R2 (C, D, A, B, X[11], 14, UINT32_C (0x265e5a51)); - MD5STEP_R2 (B, C, D, A, X[0], 20, UINT32_C (0xe9b6c7aa)); - - MD5STEP_R2 (A, B, C, D, X[5], 5, UINT32_C (0xd62f105d)); - MD5STEP_R2 (D, A, B, C, X[10], 9, UINT32_C (0x02441453)); - MD5STEP_R2 (C, D, A, B, X[15], 14, UINT32_C (0xd8a1e681)); - MD5STEP_R2 (B, C, D, A, X[4], 20, UINT32_C (0xe7d3fbc8)); - - MD5STEP_R2 (A, B, C, D, X[9], 5, UINT32_C (0x21e1cde6)); - MD5STEP_R2 (D, A, B, C, X[14], 9, UINT32_C (0xc33707d6)); - MD5STEP_R2 (C, D, A, B, X[3], 14, UINT32_C (0xf4d50d87)); - MD5STEP_R2 (B, C, D, A, X[8], 20, UINT32_C (0x455a14ed)); - - MD5STEP_R2 (A, B, C, D, X[13], 5, UINT32_C (0xa9e3e905)); - MD5STEP_R2 (D, A, B, C, X[2], 9, UINT32_C (0xfcefa3f8)); - MD5STEP_R2 (C, D, A, B, X[7], 14, UINT32_C (0x676f02d9)); - MD5STEP_R2 (B, C, D, A, X[12], 20, UINT32_C (0x8d2a4c8a)); - - /* Round 3. */ - - MD5STEP_R3 (A, B, C, D, X[5], 4, UINT32_C (0xfffa3942)); - MD5STEP_R3 (D, A, B, C, X[8], 11, UINT32_C (0x8771f681)); - MD5STEP_R3 (C, D, A, B, X[11], 16, UINT32_C (0x6d9d6122)); - MD5STEP_R3 (B, C, D, A, X[14], 23, UINT32_C (0xfde5380c)); - - MD5STEP_R3 (A, B, C, D, X[1], 4, UINT32_C (0xa4beea44)); - MD5STEP_R3 (D, A, B, C, X[4], 11, UINT32_C (0x4bdecfa9)); - MD5STEP_R3 (C, D, A, B, X[7], 16, UINT32_C (0xf6bb4b60)); - MD5STEP_R3 (B, C, D, A, X[10], 23, UINT32_C (0xbebfbc70)); - - MD5STEP_R3 (A, B, C, D, X[13], 4, UINT32_C (0x289b7ec6)); - MD5STEP_R3 (D, A, B, C, X[0], 11, UINT32_C (0xeaa127fa)); - MD5STEP_R3 (C, D, A, B, X[3], 16, UINT32_C (0xd4ef3085)); - MD5STEP_R3 (B, C, D, A, X[6], 23, UINT32_C (0x04881d05)); - - MD5STEP_R3 (A, B, C, D, X[9], 4, UINT32_C (0xd9d4d039)); - MD5STEP_R3 (D, A, B, C, X[12], 11, UINT32_C (0xe6db99e5)); - MD5STEP_R3 (C, D, A, B, X[15], 16, UINT32_C (0x1fa27cf8)); - MD5STEP_R3 (B, C, D, A, X[2], 23, UINT32_C (0xc4ac5665)); - - /* Round 4. */ - - MD5STEP_R4 (A, B, C, D, X[0], 6, UINT32_C (0xf4292244)); - MD5STEP_R4 (D, A, B, C, X[7], 10, UINT32_C (0x432aff97)); - MD5STEP_R4 (C, D, A, B, X[14], 15, UINT32_C (0xab9423a7)); - MD5STEP_R4 (B, C, D, A, X[5], 21, UINT32_C (0xfc93a039)); - - MD5STEP_R4 (A, B, C, D, X[12], 6, UINT32_C (0x655b59c3)); - MD5STEP_R4 (D, A, B, C, X[3], 10, UINT32_C (0x8f0ccc92)); - MD5STEP_R4 (C, D, A, B, X[10], 15, UINT32_C (0xffeff47d)); - MD5STEP_R4 (B, C, D, A, X[1], 21, UINT32_C (0x85845dd1)); - - MD5STEP_R4 (A, B, C, D, X[8], 6, UINT32_C (0x6fa87e4f)); - MD5STEP_R4 (D, A, B, C, X[15], 10, UINT32_C (0xfe2ce6e0)); - MD5STEP_R4 (C, D, A, B, X[6], 15, UINT32_C (0xa3014314)); - MD5STEP_R4 (B, C, D, A, X[13], 21, UINT32_C (0x4e0811a1)); - - MD5STEP_R4 (A, B, C, D, X[4], 6, UINT32_C (0xf7537e82)); - MD5STEP_R4 (D, A, B, C, X[11], 10, UINT32_C (0xbd3af235)); - MD5STEP_R4 (C, D, A, B, X[2], 15, UINT32_C (0x2ad7d2bb)); - MD5STEP_R4 (B, C, D, A, X[9], 21, UINT32_C (0xeb86d391)); -#else /* MHD_FAVOR_SMALL_CODE */ - if (1) - { - static const uint32_t T[64] = - { UINT32_C (0xd76aa478), UINT32_C (0xe8c7b756), UINT32_C (0x242070db), - UINT32_C (0xc1bdceee), UINT32_C (0xf57c0faf), UINT32_C (0x4787c62a), - UINT32_C (0xa8304613), UINT32_C (0xfd469501), UINT32_C (0x698098d8), - UINT32_C (0x8b44f7af), UINT32_C (0xffff5bb1), UINT32_C (0x895cd7be), - UINT32_C (0x6b901122), UINT32_C (0xfd987193), UINT32_C (0xa679438e), - UINT32_C (0x49b40821), UINT32_C (0xf61e2562), UINT32_C (0xc040b340), - UINT32_C (0x265e5a51), UINT32_C (0xe9b6c7aa), UINT32_C (0xd62f105d), - UINT32_C (0x02441453), UINT32_C (0xd8a1e681), UINT32_C (0xe7d3fbc8), - UINT32_C (0x21e1cde6), UINT32_C (0xc33707d6), UINT32_C (0xf4d50d87), - UINT32_C (0x455a14ed), UINT32_C (0xa9e3e905), UINT32_C (0xfcefa3f8), - UINT32_C (0x676f02d9), UINT32_C (0x8d2a4c8a), UINT32_C (0xfffa3942), - UINT32_C (0x8771f681), UINT32_C (0x6d9d6122), UINT32_C (0xfde5380c), - UINT32_C (0xa4beea44), UINT32_C (0x4bdecfa9), UINT32_C (0xf6bb4b60), - UINT32_C (0xbebfbc70), UINT32_C (0x289b7ec6), UINT32_C (0xeaa127fa), - UINT32_C (0xd4ef3085), UINT32_C (0x04881d05), UINT32_C (0xd9d4d039), - UINT32_C (0xe6db99e5), UINT32_C (0x1fa27cf8), UINT32_C (0xc4ac5665), - UINT32_C (0xf4292244), UINT32_C (0x432aff97), UINT32_C (0xab9423a7), - UINT32_C (0xfc93a039), UINT32_C (0x655b59c3), UINT32_C (0x8f0ccc92), - UINT32_C (0xffeff47d), UINT32_C (0x85845dd1), UINT32_C (0x6fa87e4f), - UINT32_C (0xfe2ce6e0), UINT32_C (0xa3014314), UINT32_C (0x4e0811a1), - UINT32_C (0xf7537e82), UINT32_C (0xbd3af235), UINT32_C (0x2ad7d2bb), - UINT32_C (0xeb86d391) }; - unsigned int i; /**< Zero-based index */ - - /* Round 1. */ - - i = 0; - do - { - /* The input data is loaded in correct (little-endian) format before - calculations on each step. */ - MD5STEP_R1 (A, B, C, D, X[i] = GET_X_FROM_DATA (M, i), 7, T[i]); - ++i; - MD5STEP_R1 (D, A, B, C, X[i] = GET_X_FROM_DATA (M, i), 12, T[i]); - ++i; - MD5STEP_R1 (C, D, A, B, X[i] = GET_X_FROM_DATA (M, i), 17, T[i]); - ++i; - MD5STEP_R1 (B, C, D, A, X[i] = GET_X_FROM_DATA (M, i), 22, T[i]); - ++i; - } while (i < 16); - - /* Round 2. */ - - do - { - const unsigned int idx_add = i; - MD5STEP_R2 (A, B, C, D, X[(1U + idx_add) & 15U], 5, T[i]); - ++i; - MD5STEP_R2 (D, A, B, C, X[(6U + idx_add) & 15U], 9, T[i]); - ++i; - MD5STEP_R2 (C, D, A, B, X[(11U + idx_add) & 15U], 14, T[i]); - ++i; - MD5STEP_R2 (B, C, D, A, X[(0U + idx_add) & 15U], 20, T[i]); - ++i; - } while (i < 32); - - /* Round 3. */ - - do - { - const unsigned int idx_add = i; - MD5STEP_R3 (A, B, C, D, X[(5U + 64U - idx_add) & 15U], 4, T[i]); - ++i; - MD5STEP_R3 (D, A, B, C, X[(8U + 64U - idx_add) & 15U], 11, T[i]); - ++i; - MD5STEP_R3 (C, D, A, B, X[(11U + 64U - idx_add) & 15U], 16, T[i]); - ++i; - MD5STEP_R3 (B, C, D, A, X[(14U + 64U - idx_add) & 15U], 23, T[i]); - ++i; - } while (i < 48); - - /* Round 4. */ - - do - { - const unsigned int idx_add = i; - MD5STEP_R4 (A, B, C, D, X[(0U + 64U - idx_add) & 15U], 6, T[i]); - ++i; - MD5STEP_R4 (D, A, B, C, X[(7U + 64U - idx_add) & 15U], 10, T[i]); - ++i; - MD5STEP_R4 (C, D, A, B, X[(14U + 64U - idx_add) & 15U], 15, T[i]); - ++i; - MD5STEP_R4 (B, C, D, A, X[(5U + 64U - idx_add) & 15U], 21, T[i]); - ++i; - } while (i < 64); - } -#endif /* MHD_FAVOR_SMALL_CODE */ - - /* Finally increment and store working variables. - See RFC 1321, end of Clause 3.4 (step 4). */ - - H[0] += A; - H[1] += B; - H[2] += C; - H[3] += D; -} - - -MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ -MHD_FN_PAR_IN_SIZE_ (3, 2) void -mhd_MD5_update (struct mhd_Md5CtxInt *restrict ctx, - size_t size, - const uint8_t *restrict data) -{ - unsigned int bytes_have; /**< Number of bytes in the context buffer */ - - mhd_assert (0 != size); - - /* Note: (count & (mhd_MD5_BLOCK_SIZE-1)) - equals (count % mhd_MD5_BLOCK_SIZE) for this block size. */ - bytes_have = (unsigned int)(ctx->count & (mhd_MD5_BLOCK_SIZE - 1)); - ctx->count += size; - - if (0 != bytes_have) - { - unsigned int bytes_left = mhd_MD5_BLOCK_SIZE - bytes_have; - if (size >= bytes_left) - { /* Combine new data with data in the buffer and - process the full block. */ - memcpy (((uint8_t *)ctx->buffer) + bytes_have, - data, - bytes_left); - data += bytes_left; - size -= bytes_left; - md5_transform (ctx->H, ctx->buffer); - bytes_have = 0; - } - } - - while (mhd_MD5_BLOCK_SIZE <= size) - { /* Process any full blocks of new data directly, - without copying to the buffer. */ - md5_transform (ctx->H, data); - data += mhd_MD5_BLOCK_SIZE; - size -= mhd_MD5_BLOCK_SIZE; - } - - if (0 != size) - { /* Copy incomplete block of new data (if any) - to the buffer. */ - memcpy (((uint8_t *)ctx->buffer) + bytes_have, data, size); - } -} - - -/** - * Size of "length" insertion in bits. - * See RFC 1321, end of Clause 3.2 (step 2). - */ -#define MD5_SIZE_OF_LEN_ADD_BITS 64 - -/** - * Size of "length" insertion in bytes. - */ -#define MD5_SIZE_OF_LEN_ADD (MD5_SIZE_OF_LEN_ADD_BITS / 8) - -MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ -MHD_FN_PAR_OUT_ (2) void -mhd_MD5_finish (struct mhd_Md5CtxInt *restrict ctx, - uint8_t digest[mhd_MD5_DIGEST_SIZE]) -{ - uint64_t num_bits; /**< Number of processed bits */ - unsigned int bytes_have; /**< Number of bytes in the context buffer */ - - /* Memorise the number of processed bits. - The padding and other data added here during the postprocessing must - not change the amount of hashed data. */ - num_bits = ctx->count << 3; - - /* Note: (count & (mhd_MD5_BLOCK_SIZE-1)) - equals (count % mhd_MD5_BLOCK_SIZE) for this block size. */ - bytes_have = (unsigned int)(ctx->count & (mhd_MD5_BLOCK_SIZE - 1)); - - /* Input data must be padded with a single bit "1", then with zeros and - the finally the length of data in bits must be added as the final bytes - of the last block. - See RFC 1321, Clauses 3.1 and 3.2 (steps 1 and 2). */ - /* Data is always processed in form of bytes (not by individual bits), - therefore position of the first padding bit in byte is always - predefined (0x80). */ - /* Buffer always have space for one byte at least (as full buffers are - processed immediately). */ - ((uint8_t *)ctx->buffer)[bytes_have++] = 0x80; - - if (mhd_MD5_BLOCK_SIZE - bytes_have < MD5_SIZE_OF_LEN_ADD) - { /* No space in the current block to put the total length of message. - Pad the current block with zeros and process it. */ - if (bytes_have < mhd_MD5_BLOCK_SIZE) - memset (((uint8_t *)ctx->buffer) + bytes_have, 0, - mhd_MD5_BLOCK_SIZE - bytes_have); - /* Process the full block. */ - md5_transform (ctx->H, ctx->buffer); - /* Start the new block. */ - bytes_have = 0; - } - - /* Pad the rest of the buffer with zeros. */ - memset (((uint8_t *)ctx->buffer) + bytes_have, 0, - mhd_MD5_BLOCK_SIZE - MD5_SIZE_OF_LEN_ADD - bytes_have); - /* Put the number of bits in processed data as little-endian value. - See RFC 1321, clauses 2 and 3.2 (step 2). */ - mhd_PUT_64BIT_LE_UNALIGN (ctx->buffer + mhd_MD5_BLOCK_SIZE_WORDS - 2, - num_bits); - /* Process the full final block. */ - md5_transform (ctx->H, ctx->buffer); - - /* Put in LE mode the hash as the final digest. - See RFC 1321, clauses 2 and 3.5 (step 5). */ - if (1) - { - bool use_tmp_buf_to_align_result; - -#if defined(mhd_PUT_32BIT_LE_UNALIGNED) - use_tmp_buf_to_align_result = false; -#elif defined(MHD_FAVOR_SMALL_CODE) - use_tmp_buf_to_align_result = true; /* smaller code: eliminated branch below */ -#else - use_tmp_buf_to_align_result = - (0 != ((uintptr_t)digest) % mhd_UINT32_ALIGN); -#endif - if (use_tmp_buf_to_align_result) - { - /* If storing of the final result requires aligned address and - the destination address is not aligned or compact code is used, - store the final digest in aligned temporary buffer first, then - copy it to the destination. */ - uint32_t alig_dgst[mhd_MD5_DIGEST_SIZE_WORDS]; - mhd_PUT_32BIT_LE (alig_dgst + 0, ctx->H[0]); - mhd_PUT_32BIT_LE (alig_dgst + 1, ctx->H[1]); - mhd_PUT_32BIT_LE (alig_dgst + 2, ctx->H[2]); - mhd_PUT_32BIT_LE (alig_dgst + 3, ctx->H[3]); - /* Copy result to the unaligned destination address. */ - memcpy (digest, alig_dgst, mhd_MD5_DIGEST_SIZE); - } - else - { - /* Use cast to (void*) here to mute compiler alignment warnings. - * Compilers are not smart enough to see that alignment has been checked. */ - mhd_PUT_32BIT_LE ((void *)(digest + 0 * mhd_MD5_BYTES_IN_WORD), \ - ctx->H[0]); - mhd_PUT_32BIT_LE ((void *)(digest + 1 * mhd_MD5_BYTES_IN_WORD), \ - ctx->H[1]); - mhd_PUT_32BIT_LE ((void *)(digest + 2 * mhd_MD5_BYTES_IN_WORD), \ - ctx->H[2]); - mhd_PUT_32BIT_LE ((void *)(digest + 3 * mhd_MD5_BYTES_IN_WORD), \ - ctx->H[3]); - } - } - - /* Erase potentially sensitive data. */ - memset (ctx, 0, sizeof(struct mhd_Md5CtxInt)); -} - - -mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE diff --git a/src/mhd2/md5_int.h b/src/mhd2/md5_int.h @@ -1,147 +0,0 @@ -/* 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) 2022-2024 Evgeny Grin (Karlson2k) - - GNU libmicrohttpd is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - GNU libmicrohttpd is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - 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/md5_int.h - * @brief Calculation of MD5 digest - * @author Karlson2k (Evgeny Grin) - */ - -#ifndef MHD_MD5_INT_H -#define MHD_MD5_INT_H 1 - -#include "mhd_sys_options.h" - -#include "sys_base_types.h" - -/** - * Number of bits in single MD5 word. - */ -#define mhd_MD5_WORD_SIZE_BITS (32) - -/** - * Number of bytes in single MD5 word. - */ -#define mhd_MD5_BYTES_IN_WORD (mhd_MD5_WORD_SIZE_BITS / 8) - -/** - * Hash is kept internally as four 32-bit words. - * This is intermediate hash size, used during computing the final digest. - */ -#define mhd_MD5_HASH_SIZE_WORDS (4) - -/** - * Size of MD5 resulting digest in bytes. - * This is the final digest size, not intermediate hash. - */ -#define mhd_MD5_DIGEST_SIZE_WORDS mhd_MD5_HASH_SIZE_WORDS - -/** - * Size of MD5 resulting digest in bytes - * This is the final digest size, not intermediate hash. - */ -#define mhd_MD5_DIGEST_SIZE (mhd_MD5_DIGEST_SIZE_WORDS * mhd_MD5_BYTES_IN_WORD) - -/** - * Size of MD5 single processing block in bits. - */ -#define mhd_MD5_BLOCK_SIZE_BITS 512 - -/** - * Size of MD5 single processing block in bytes. - */ -#define mhd_MD5_BLOCK_SIZE (mhd_MD5_BLOCK_SIZE_BITS / 8) - -/** - * Size of MD5 single processing block in words. - */ -#define mhd_MD5_BLOCK_SIZE_WORDS \ - (mhd_MD5_BLOCK_SIZE_BITS / mhd_MD5_WORD_SIZE_BITS) - - -/** - * MD5 calculation context - */ -struct mhd_Md5CtxInt -{ - uint32_t H[mhd_MD5_HASH_SIZE_WORDS]; /**< Intermediate hash value / digest at end of calculation */ - uint32_t buffer[mhd_MD5_BLOCK_SIZE_WORDS]; /**< MD5 input data buffer */ - uint64_t count; /**< number of bytes, mod 2^64 */ -}; - -/** - * Initialise structure for MD5 calculation. - * - * @param ctx the calculation context - */ -MHD_INTERNAL void -mhd_MD5_init (struct mhd_Md5CtxInt *ctx) -MHD_FN_PAR_NONNULL_ALL_; - - -/** - * MD5 process portion of bytes. - * - * @param ctx the calculation context - * @param size number of bytes in @a data - * @param data bytes to add to hash - */ -MHD_INTERNAL void -mhd_MD5_update (struct mhd_Md5CtxInt *restrict ctx, - size_t size, - const uint8_t *restrict data) -MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3, 2); - - -/** - * Finalise MD5 calculation, return digest. - * - * @param ctx the calculation context - * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes - */ -MHD_INTERNAL void -mhd_MD5_finish (struct mhd_Md5CtxInt *restrict ctx, - uint8_t digest[mhd_MD5_DIGEST_SIZE]) -MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2); - -/** - * Indicates that function mhd_MD5_finish() (without context reset) is available - */ -#define mhd_MD5_HAS_FINISH 1 - -#endif /* MHD_MD5_INT_H */ diff --git a/src/mhd2/md5_mbedtls.c b/src/mhd2/md5_mbedtls.c @@ -0,0 +1,109 @@ +/* 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) 2026 Evgeny Grin (Karlson2k) + 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/md5_mbedtls.c + * @brief Wrapper for MD5 calculation performed by mbedTLS library + * @author Karlson2k (Evgeny Grin) + * @author Christian Grothoff + */ + +#include "mhd_sys_options.h" + +#include "mhd_assert.h" + +#include "md5_mbedtls.h" + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (1) void +mhd_MD5_mtls_init (struct mhd_Md5CtxMtls *ctx) +{ + mbedtls_md5_init (&(ctx->mbed_ctx)); + ctx->ext_error = (0 != mbedtls_md5_starts (&(ctx->mbed_ctx))); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) void +mhd_MD5_mtls_reset (struct mhd_Md5CtxMtls *ctx) +{ + if (ctx->ext_error) + return; + + ctx->ext_error = (0 != mbedtls_md5_starts (&(ctx->mbed_ctx))); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) +MHD_FN_PAR_IN_SIZE_ (3, 2) void +mhd_MD5_mtls_update (struct mhd_Md5CtxMtls *restrict ctx, + size_t size, + const void *restrict data) +{ +#ifndef MHD_UNIT_TESTING + mhd_assert (0 != size); +#endif + + if (ctx->ext_error) + return; + + ctx->ext_error = (0 != mbedtls_md5_update (&(ctx->mbed_ctx), + (const unsigned char *)data, + size)); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) +MHD_FN_PAR_OUT_ (2) void +mhd_MD5_mtls_finish ( + struct mhd_Md5CtxMtls *restrict ctx, + uint8_t digest[MHD_FN_PAR_FIX_ARR_SIZE_ (mhd_MD5_DIGEST_SIZE)]) +{ + if (ctx->ext_error) + return; + + ctx->ext_error = (0 != mbedtls_md5_finish (&(ctx->mbed_ctx), + (unsigned char *)digest)); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) void +mhd_MD5_mtls_deinit (struct mhd_Md5CtxMtls *ctx) +{ + mbedtls_md5_free (&(ctx->mbed_ctx)); +} diff --git a/src/mhd2/md5_mbedtls.h b/src/mhd2/md5_mbedtls.h @@ -0,0 +1,167 @@ +/* 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) 2022-2026 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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/md5_mbedtls.h + * @brief Wrapper declarations for MD5 calculation performed by mbedTLS backend + * @author Karlson2k (Evgeny Grin) + */ +#ifndef MHD_MD5_MBEDTLS_H +#define MHD_MD5_MBEDTLS_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +/* MbedTLS header is small in size and scope, it is directly included here */ +#include <mbedtls/md5.h> + +#ifndef mhd_MD5_DIGEST_SIZE +/** + * Size of MD5 resulting digest in bytes + * This is the final digest size, not the intermediate hash. + */ +# define mhd_MD5_DIGEST_SIZE (16) +#endif + +/** + * MD5 calculation context + */ +struct mhd_Md5CtxMtls +{ + struct mbedtls_md5_context mbed_ctx; /**< Hash calculation handle */ + bool ext_error; /**< 'true' if the hashing backend has reported an error */ +}; + +/** + * Indicates whether struct mhd_Md5CtxMtls has an 'ext_error' member. + * + * Set to '1' when the structure has an 'ext_error' member. + */ +#define mhd_MD5_MTLS_EXT_ERROR_FLAG 1 + +/** + * Initialise the context for MD5 calculation. + * + * This function must not be called more than once for @a ctx without an + * intervening #mhd_MD5_mtls_deinit(). + * + * Whether or not an error is reported, #mhd_MD5_mtls_deinit() must be + * called for @a ctx once this function has returned. + * + * The error state in @a ctx is set according to the result: cleared on + * success, set on failure. + * + * @param ctx the calculation context + */ +MHD_INTERNAL void +mhd_MD5_mtls_init (struct mhd_Md5CtxMtls *ctx) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (1); + + +/** + * Add a portion of data to the MD5 calculation. + * + * If @a ctx already has a recorded error, this function does nothing. + * Otherwise, if the operation fails, the error is recorded in @a ctx. + * + * @param ctx the calculation context + * @param size number of bytes in @a data, must not be 0 + * @param data bytes to add to hash + */ +MHD_INTERNAL void +mhd_MD5_mtls_update (struct mhd_Md5CtxMtls *restrict ctx, + size_t size, + const void *restrict data) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_IN_SIZE_ (3, 2); + +/** + * Finalise the MD5 calculation and return the digest. + * + * If @a ctx already has a recorded error, this function does nothing. + * Otherwise, if the operation fails, the error is recorded in @a ctx. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes + */ +MHD_INTERNAL void +mhd_MD5_mtls_finish ( + struct mhd_Md5CtxMtls *restrict ctx, + uint8_t digest[MHD_FN_PAR_FIX_ARR_SIZE_ (mhd_MD5_DIGEST_SIZE)]) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_OUT_ (2); + +/** + * Reset the context for a new MD5 calculation. + * + * This function may be called only after #mhd_MD5_mtls_finish(). If the context + * is subsequently reused for a new calculation, this function must be called + * before starting that calculation. + * + * If @a ctx already has a recorded error, this function does nothing and the + * error is preserved. Otherwise, if the operation fails, the error is recorded + * in @a ctx. + * + * @param ctx the calculation context + */ +MHD_INTERNAL void +mhd_MD5_mtls_reset (struct mhd_Md5CtxMtls *ctx) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); + +/** + * Deinitialise the MD5 calculation context. + * + * After calling this function, @a ctx can be reinitialised with + * #mhd_MD5_mtls_init(). + * + * The recorded error state in @a ctx is preserved. + * + * @param ctx the calculation context + */ +MHD_INTERNAL void +mhd_MD5_mtls_deinit (struct mhd_Md5CtxMtls *ctx) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); + +/** + * Indicates whether #mhd_MD5_mtls_deinit() is a real function or a no-op. + * + * Set to '0' as #mhd_MD5_mtls_deinit() is a real function. + */ +#define mhd_MD5_MTLS_DEINIT_NOOP_FLAG 0 + + +#endif /* MHD_MD5_MBEDTLS_H */ diff --git a/src/mhd2/md5_openssl.c b/src/mhd2/md5_openssl.c @@ -0,0 +1,132 @@ +/* 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) 2026 Evgeny Grin (Karlson2k) + 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/md5_openssl.c + * @brief Wrapper for MD5 calculation performed by OpenSSL backend + * @author Karlson2k (Evgeny Grin) + * @author Christian Grothoff + */ + +#include "mhd_sys_options.h" + +#include "mhd_assert.h" + +#include <openssl/evp.h> + +#include "md5_openssl.h" + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (1) void +mhd_MD5_ossl_init (struct mhd_Md5CtxOssl *ctx) +{ + ctx->ossl_ctx = EVP_MD_CTX_new (); + ctx->ext_error = (NULL == ctx->ossl_ctx); + + mhd_MD5_ossl_reset (ctx); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) void +mhd_MD5_ossl_reset (struct mhd_Md5CtxOssl *ctx) +{ + if (ctx->ext_error) + return; + + mhd_assert (NULL != ctx->ossl_ctx); + + ctx->ext_error = (0 == EVP_DigestInit_ex (ctx->ossl_ctx, + EVP_md5 (), + NULL)); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) +MHD_FN_PAR_IN_SIZE_ (3, 2) void +mhd_MD5_ossl_update (struct mhd_Md5CtxOssl *restrict ctx, + size_t size, + const void *restrict data) +{ +#ifndef MHD_UNIT_TESTING + mhd_assert (0 != size); +#endif + + if (ctx->ext_error) + return; + + mhd_assert (NULL != ctx->ossl_ctx); + + ctx->ext_error = (0 == EVP_DigestUpdate (ctx->ossl_ctx, + data, + size)); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) +MHD_FN_PAR_OUT_ (2) void +mhd_MD5_ossl_finish ( + struct mhd_Md5CtxOssl *restrict ctx, + uint8_t digest[MHD_FN_PAR_FIX_ARR_SIZE_ (mhd_MD5_DIGEST_SIZE)]) +{ + if (!ctx->ext_error) + { +#ifndef NDEBUG + unsigned int len; + + mhd_assert (NULL != ctx->ossl_ctx); + + ctx->ext_error = (0 == EVP_DigestFinal_ex (ctx->ossl_ctx, + digest, + &len)); + mhd_assert ((ctx->ext_error) || (mhd_MD5_DIGEST_SIZE == len)); +#else + ctx->ext_error = (0 == EVP_DigestFinal_ex (ctx->ossl_ctx, + digest, + NULL)); +#endif + } +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) void +mhd_MD5_ossl_deinit (struct mhd_Md5CtxOssl *ctx) +{ + EVP_MD_CTX_free (ctx->ossl_ctx); + ctx->ossl_ctx = NULL; +} diff --git a/src/mhd2/md5_openssl.h b/src/mhd2/md5_openssl.h @@ -0,0 +1,166 @@ +/* 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) 2022-2026 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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/md5_openssl.h + * @brief Wrapper declarations for MD5 calculation performed by OpenSSL backend + * @author Karlson2k (Evgeny Grin) + */ +#ifndef MHD_MD5_OPENSSL_H +#define MHD_MD5_OPENSSL_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#ifndef mhd_MD5_DIGEST_SIZE +/** + * Size of MD5 resulting digest in bytes + * This is the final digest size, not the intermediate hash. + */ +# define mhd_MD5_DIGEST_SIZE (16) +#endif + +/* Forward declaration for OpenSSL struct, to avoid including large header */ +struct evp_md_ctx_st; + +/** + * MD5 calculation context + */ +struct mhd_Md5CtxOssl +{ + struct evp_md_ctx_st *ossl_ctx; /**< Hash calculation handle */ + bool ext_error; /**< 'true' if the hashing backend has reported an error */ +}; + +/** + * Indicates whether struct mhd_Md5CtxOssl has an 'ext_error' member. + * + * Set to '1' when the structure has an 'ext_error' member. + */ +#define mhd_MD5_OSSL_EXT_ERROR_FLAG 1 + +/** + * Initialise the context for MD5 calculation. + * + * This function must not be called more than once for @a ctx without an + * intervening #mhd_MD5_ossl_deinit(). + * + * Whether or not an error is reported, #mhd_MD5_ossl_deinit() must be + * called for @a ctx once this function has returned. + * + * The error state in @a ctx is set according to the result: cleared on + * success, set on failure. + * + * @param ctx the calculation context + */ +MHD_INTERNAL void +mhd_MD5_ossl_init (struct mhd_Md5CtxOssl *ctx) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (1); + +/** + * Add a portion of data to the MD5 calculation. + * + * If @a ctx already has a recorded error, this function does nothing. + * Otherwise, if the operation fails, the error is recorded in @a ctx. + * + * @param ctx the calculation context + * @param size number of bytes in @a data, must not be 0 + * @param data bytes to add to hash + */ +MHD_INTERNAL void +mhd_MD5_ossl_update (struct mhd_Md5CtxOssl *restrict ctx, + size_t size, + const void *restrict data) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_IN_SIZE_ (3, 2); + +/** + * Finalise the MD5 calculation and return the digest. + * + * If @a ctx already has a recorded error, this function does nothing. + * Otherwise, if the operation fails, the error is recorded in @a ctx. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes + */ +MHD_INTERNAL void +mhd_MD5_ossl_finish ( + struct mhd_Md5CtxOssl *restrict ctx, + uint8_t digest[MHD_FN_PAR_FIX_ARR_SIZE_ (mhd_MD5_DIGEST_SIZE)]) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1) MHD_FN_PAR_OUT_ (2); + +/** + * Reset the context for a new MD5 calculation. + * + * This function may be called only after #mhd_MD5_ossl_finish(). If the context + * is subsequently reused for a new calculation, this function must be called + * before starting that calculation. + * + * If @a ctx already has a recorded error, this function does nothing and the + * error is preserved. Otherwise, if the operation fails, the error is recorded + * in @a ctx. + * + * @param ctx the calculation context + */ +MHD_INTERNAL void +mhd_MD5_ossl_reset (struct mhd_Md5CtxOssl *ctx) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); + +/** + * Deinitialise the MD5 calculation context. + * + * After calling this function, @a ctx can be reinitialised with + * #mhd_MD5_ossl_init(). + * + * The recorded error state in @a ctx is preserved. + * + * @param ctx the calculation context + */ +MHD_INTERNAL void +mhd_MD5_ossl_deinit (struct mhd_Md5CtxOssl *ctx) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); + +/** + * Indicates whether #mhd_MD5_ossl_deinit() is a real function or a no-op. + * + * Set to '0' as #mhd_MD5_ossl_deinit() is a real function. + */ +#define mhd_MD5_OSSL_DEINIT_NOOP_FLAG 0 + + +#endif /* MHD_MD5_OPENSSL_H */ diff --git a/src/mhd2/mhd_macro_concat.h b/src/mhd2/mhd_macro_concat.h @@ -49,6 +49,16 @@ /** + * Concatenate two arguments literally + */ +#define mhd_MACRO_CONCAT_(a, b) a ## b +/** + * Concatenate two arguments after expansion + */ +#define mhd_MACRO_CONCAT(a, b) mhd_MACRO_CONCAT_ (a,b) + + +/** * Concatenate three arguments literally */ #define mhd_MACRO_CONCAT3_(a, b, c) a ## b ## c diff --git a/src/mhd2/mhd_md5.h b/src/mhd2/mhd_md5.h @@ -1,7 +1,7 @@ /* 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) 2022-2024 Evgeny Grin (Karlson2k) + Copyright (C) 2022-2026 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 @@ -38,7 +38,14 @@ /** * @file src/mhd2/mhd_md5.h - * @brief Simple wrapper for selection of built-in/external MD5 implementation + * @brief Simple wrapper for selecting the MD5 implementation backend + * + * Usage: declare a context as 'struct mhd_Md5Ctx', then call mhd_MD5_init(), + * mhd_MD5_update() (any number of times) and mhd_MD5_finish() to get the + * digest. Reuse the context with mhd_MD5_reset() or release it with + * mhd_MD5_deinit(). With some backends an operation can fail; check with + * mhd_MD5_has_err(). + * * @author Karlson2k (Evgeny Grin) */ @@ -51,82 +58,218 @@ # error This file must be used only when MD5 is enabled #endif -#ifndef MHD_MD5_EXTR -# include "md5_int.h" -#else /* MHD_MD5_EXTR */ -# include "md5_ext.h" -#endif /* MHD_MD5_EXTR */ +#include "mhd_macro_concat.h" -#ifndef mhd_MD5_DIGEST_SIZE +#if defined(MHD_MD5_EXTR_GNUTLS) +# include "md5_gnutls.h" + +/** + * The hashing backend identifier for function names + */ +# define mhd_MD5_FUNC_NAME_ID gtls /** - * Size of MD5 resulting digest in bytes - * This is the final digest size, not intermediate hash. + * The hashing backend identifier for data names */ -# define mhd_MD5_DIGEST_SIZE (16) -#endif /* ! mhd_MD5_DIGEST_SIZE */ +# define mhd_MD5_DATA_NAME_ID Gtls +/** + * The hashing backend identifier for macro names + */ +# define mhd_MD5_MACRO_NAME_ID GTLS + +#elif defined(MHD_MD5_EXTR_OPENSSL) +# include "md5_openssl.h" -#ifndef MHD_MD5_EXTR /** - * Universal ctx type mapped for chosen implementation + * The hashing backend identifier for function names + */ +# define mhd_MD5_FUNC_NAME_ID ossl +/** + * The hashing backend identifier for data names */ -# define mhd_Md5Ctx mhd_Md5CtxInt -#else /* MHD_MD5_EXTR */ +# define mhd_MD5_DATA_NAME_ID Ossl /** - * Universal ctx type mapped for chosen implementation + * The hashing backend identifier for macro names */ -# define mhd_Md5Ctx mhd_Md5CtxExt -#endif /* MHD_MD5_EXTR */ +# define mhd_MD5_MACRO_NAME_ID OSSL + +#elif defined(MHD_MD5_EXTR_MBEDTLS) +# include "md5_mbedtls.h" -#ifndef mhd_MD5_HAS_INIT_ONE_TIME /** - * Setup and prepare ctx for hash calculation + * The hashing backend identifier for function names + */ +# define mhd_MD5_FUNC_NAME_ID mtls +/** + * The hashing backend identifier for data names */ -# define mhd_MD5_init_one_time(ctx) mhd_MD5_init (ctx) -#endif /* ! mhd_MD5_HAS_INIT_ONE_TIME */ +# define mhd_MD5_DATA_NAME_ID Mtls +/** + * The hashing backend identifier for macro names + */ +# define mhd_MD5_MACRO_NAME_ID MTLS +#elif !defined(MHD_MD5_EXTR) +# include "md5_builtin.h" -#ifndef mhd_MD5_HAS_FINISH_RESET /** - * Re-use the same ctx for the new hashing after digest calculated + * The hashing backend identifier for function names */ -# define mhd_MD5_reset(ctx) mhd_MD5_init (ctx) +# define mhd_MD5_FUNC_NAME_ID blti /** - * Finalise MD5 calculation, return digest, reset hash calculation. + * The hashing backend identifier for data names */ -# define mhd_MD5_finish_reset(ctx, digest) \ - (mhd_MD5_finish (ctx,digest), mhd_MD5_reset (ctx)) +# define mhd_MD5_DATA_NAME_ID Blti /** - * Finalise hash calculation, return digest, de-initialise hash calculation. + * The hashing backend identifier for macro names */ -# define mhd_MD5_finish_deinit(ctx, digest) \ - (mhd_MD5_finish (ctx,digest), mhd_MD5_deinit (ctx)) -#else /* mhd_MD5_HAS_FINISH_RESET */ +# define mhd_MD5_MACRO_NAME_ID BLTI +#else +# error No MD5 implementation enabled +#endif + +#if mhd_MD5_DIGEST_SIZE != 16 +# error Wrong value of mhd_MD5_DIGEST_SIZE +#endif + /** - * Finalise MD5 calculation, return digest + * Form the name of the function specific to the selected hashing backend */ -# define mhd_MD5_finish(ctx, digest) mhd_MD5_finish_reset(ctx, digest) -# define mhd_MD5_reset(ctx) ((void) 0) +#define mhd_MD5_FUNC(name_suffix) \ + mhd_MACRO_CONCAT3 (mhd_MD5_,mhd_MD5_FUNC_NAME_ID,name_suffix) + /** - * Finalise hash calculation, return digest, de-initialise hash calculation. + * Form the name of the macro specific to the selected hashing backend */ -# define mhd_MD5_finish_deinit(ctx, digest) \ - (mhd_MD5_finish_reset (ctx,digest), mhd_MD5_deinit (ctx)) -#endif /* mhd_MD5_HAS_FINISH_RESET */ +#define mhd_MD5_MACRO(name_suffix) \ + mhd_MACRO_CONCAT3 (mhd_MD5_,mhd_MD5_MACRO_NAME_ID,name_suffix) -#ifndef mhd_MD5_HAS_DEINIT -# define mhd_MD5_deinit(ctx) ((void) 0) -#endif /* HAVE_MD5_DEINIT */ +/** + * Struct tag of the calculation context for the selected MD5 backend + */ +#define mhd_Md5Ctx mhd_MACRO_CONCAT(mhd_Md5Ctx,mhd_MD5_DATA_NAME_ID) -#ifdef mhd_MD5_HAS_EXT_ERROR -# define mhd_MD5_has_err(ctx) (0 != ((ctx)->ext_error)) -#else /* ! mhd_MD5_HAS_EXT_ERROR */ -# define mhd_MD5_has_err(ctx) (((void) (ctx)), ! ! 0) -#endif /* ! mhd_SHA512_256_HAS_EXT_ERROR */ +#if 0 != mhd_MD5_MACRO (_EXT_ERROR_FLAG) +/** + * Indicates that MD5 hashing or initialisation may result in an error + */ +# define mhd_MD5_HAS_EXT_ERROR 1 +/** + * 'true' if the hashing backend has reported an error + */ +# define mhd_MD5_has_err(ctx) (!!((ctx)->ext_error)) +/** + * Get the error number reported by hashing backend (zero if no error) + */ +# define mhd_MD5_get_err(ctx) ((int)((ctx)->ext_error)) +#else +# define mhd_MD5_has_err(ctx) (((void) (ctx)), !!0) +/** + * Get the error number (zero if no error) + */ +# define mhd_MD5_get_err(ctx) ((int)(0)) +#endif + +/** + * Initialise the context for MD5 calculation. + * + * This function must not be called more than once for @a ctx without an + * intervening #mhd_MD5_deinit(). + * + * Whether or not an error is reported, #mhd_MD5_deinit() must be + * called for @a ctx once this function has returned. + * + * For backends that track errors (see #mhd_MD5_has_err()), the error state is + * set according to the result: cleared on success, set on failure. + * + * @param ctx the calculation context + */ +#define mhd_MD5_init(ctx) mhd_MD5_FUNC(_init) ((ctx)) + +/** + * Add a portion of data to the MD5 calculation. + * + * For backends that track errors (see #mhd_MD5_has_err()), this does nothing + * if @a ctx already has a recorded error, and records an error on failure. + * + * @param ctx the calculation context + * @param size number of bytes in @a data, must not be 0 + * @param data bytes to add to hash + */ +#define mhd_MD5_update(ctx, size, data) \ + mhd_MD5_FUNC(_update) ((ctx),(size),(data)) + +/** + * Finalise the MD5 calculation and return the digest. + * + * For backends that track errors (see #mhd_MD5_has_err()), this does nothing + * if @a ctx already has a recorded error, and may record an error on failure. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes + */ +#define mhd_MD5_finish(ctx, digest) \ + mhd_MD5_FUNC(_finish) ((ctx),(digest)) + +/** + * Reset the context for a new MD5 calculation. + * + * This function may be called only after #mhd_MD5_finish(). If the context is + * subsequently reused for a new calculation, this function must be called + * before starting that calculation. + * + * For backends that track errors (see #mhd_MD5_has_err()), this does nothing + * if @a ctx already has a recorded error, and may record an error on failure. + * + * @param ctx the calculation context + */ +#define mhd_MD5_reset(ctx) mhd_MD5_FUNC(_reset) ((ctx)) + +/** + * Finalise the MD5 calculation, return the digest and reset the context. + * + * For backends that track errors (see #mhd_MD5_has_err()): if @a ctx already + * has a recorded error, this does nothing and the error is preserved. + * Otherwise, an error is recorded in @a ctx if either the finalisation or the + * reset fails. A recorded error is never cleared by this macro. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes + */ +#define mhd_MD5_finish_reset(ctx, digest) \ + (mhd_MD5_finish((ctx),(digest)), mhd_MD5_reset((ctx))) + +/** + * Deinitialise the MD5 calculation context. + * + * After calling this function, @a ctx can be reinitialised with + * #mhd_MD5_init(). + * + * For backends that track errors (see #mhd_MD5_has_err()), the recorded error + * state is preserved. + * + * @param ctx the calculation context + */ +#define mhd_MD5_deinit(ctx) mhd_MD5_FUNC(_deinit) ((ctx)) -/* Sanity checks */ +/** + * Finalise the MD5 calculation, return the digest and deinitialise the context. + * + * For backends that track errors (see #mhd_MD5_has_err()): if @a ctx already + * has a recorded error, the error is preserved. Otherwise, an error is + * recorded in @a ctx if the finalisation fails. + * + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #mhd_MD5_DIGEST_SIZE bytes + */ +#define mhd_MD5_finish_deinit(ctx, digest) \ + (mhd_MD5_finish((ctx),(digest)), mhd_MD5_deinit((ctx))) + +#if 0 == mhd_MD5_MACRO (_DEINIT_NOOP_FLAG) +/** + * Indicates that #mhd_MD5_deinit() is a real function that must be called. + */ +# define mhd_MD5_HAS_DEINIT +#endif -#if !defined(mhd_MD5_HAS_FINISH_RESET) && !defined(mhd_MD5_HAS_FINISH) -# error Required at least one of mhd_MD5_finish_reset(), mhd_MD5_finish() -#endif /* ! mhd_MD5_HAS_FINISH_RESET && ! mhd_MD5_HAS_FINISH */ #endif /* MHD_MD5_H */ diff --git a/src/tests/unit/Makefile.am b/src/tests/unit/Makefile.am @@ -112,7 +112,24 @@ unit_hpack_tables_static_SOURCES = $(unit_hpack_tables_dynamic_SOURCES) unit_hpack_tables_combined_SOURCES = $(unit_hpack_tables_dynamic_SOURCES) -unit_md5_SOURCES = unit_md5.c +unit_md5_SOURCES = \ + unit_md5.c \ + $(srcdir)/../../mhd2/mhd_md5.h \ + $(srcdir)/../../mhd2/md5_builtin.c $(srcdir)/../../mhd2/md5_builtin.h + +if MHD_SUPPORT_GNUTLS + unit_md5_SOURCES += \ + $(srcdir)/../../mhd2/md5_gnutls.c $(srcdir)/../../mhd2/md5_gnutls.h +endif +if MHD_SUPPORT_OPENSSL + unit_md5_SOURCES += \ + $(srcdir)/../../mhd2/md5_openssl.c $(srcdir)/../../mhd2/md5_openssl.h +endif +if MHD_SUPPORT_MBEDTLS + unit_md5_SOURCES += \ + $(srcdir)/../../mhd2/md5_mbedtls.c $(srcdir)/../../mhd2/md5_mbedtls.h +endif + unit_sha256_SOURCES = unit_sha256.c diff --git a/src/tests/unit/unit_md5.c b/src/tests/unit/unit_md5.c @@ -48,20 +48,9 @@ #include <stdlib.h> #include <string.h> #include <stdint.h> -#include <stdbool.h> #include "mhdt_has_param.h" -#if defined MHD_MD5_EXTR_OPENSSL -# include "../mhd2/md5_ext_openssl.c" -#elif defined MHD_MD5_EXTR_GNUTLS -# include "../mhd2/md5_ext_gnutls.c" -#elif defined MHD_MD5_EXTR_MBEDTLS -# include "../mhd2/md5_ext_mbedtls.c" -#else -# include "../mhd2/md5_int.c" -#endif - #include "../mhd2/mhd_md5.h" #define MD5_DIGEST_STRING_SIZE (mhd_MD5_DIGEST_SIZE * 2 + 1) @@ -72,9 +61,6 @@ static int verbose; -/* Define to 1 if libmicrohttpd is compiled with MD5 hashing by OpenSSL. */ -/* #undef MHD_MD5_EXTR_OPENSSL */ - /* Helper function to convert hex string to binary */ static size_t hex2bin (const char *hex, @@ -129,10 +115,10 @@ check_result (const char *test_name, const uint8_t calculated[mhd_MD5_DIGEST_SIZE], const uint8_t expected[mhd_MD5_DIGEST_SIZE]) { - bool failed = (0 != - memcmp (calculated, - expected, - mhd_MD5_DIGEST_SIZE)); + int failed = (0 != + memcmp (calculated, + expected, + mhd_MD5_DIGEST_SIZE)); check_num++; /* Print 1-based numbers */ if (failed) @@ -247,7 +233,7 @@ test1_str (void) unsigned int num_failed = 0; struct mhd_Md5Ctx ctx; - mhd_MD5_init_one_time (&ctx); + mhd_MD5_init (&ctx); for (i = 0; i < units1_num; i++) { uint8_t digest[mhd_MD5_DIGEST_SIZE]; @@ -258,15 +244,13 @@ test1_str (void) (const uint8_t *)data_units1[i].str_l.str); mhd_MD5_finish_reset (&ctx, digest); -#if defined(MHD_MD5_EXTR) if (mhd_MD5_has_err (&ctx)) { fprintf (stderr, "External hashing error: %d.\n", - ctx.ext_error); + mhd_MD5_get_err (&ctx)); exit (99); } -#endif num_failed += check_result (MHD_FUNC_, i, digest, @@ -430,7 +414,7 @@ test1_bin (void) unsigned int num_failed = 0; struct mhd_Md5Ctx ctx; - mhd_MD5_init_one_time (&ctx); + mhd_MD5_init (&ctx); for (i = 0; i < units2_num; i++) { uint8_t digest[mhd_MD5_DIGEST_SIZE]; @@ -440,15 +424,13 @@ test1_bin (void) data_units2[i].bin_l.bin); mhd_MD5_finish_reset (&ctx, digest); -#if defined(MHD_MD5_EXTR) if (mhd_MD5_has_err (&ctx)) { fprintf (stderr, "External hashing error: %d.\n", - ctx.ext_error); + mhd_MD5_get_err (&ctx)); exit (99); } -#endif num_failed += check_result (MHD_FUNC_, i, digest, @@ -467,7 +449,7 @@ test2_str (void) unsigned int num_failed = 0; struct mhd_Md5Ctx ctx; - mhd_MD5_init_one_time (&ctx); + mhd_MD5_init (&ctx); for (i = 0; i < units1_num; i++) { uint8_t digest[mhd_MD5_DIGEST_SIZE]; @@ -483,15 +465,13 @@ test2_str (void) (const uint8_t *)data_units1[i].str_l.str + part_s); mhd_MD5_finish_reset (&ctx, digest); -#if defined(MHD_MD5_EXTR) if (mhd_MD5_has_err (&ctx)) { fprintf (stderr, "External hashing error: %d.\n", - ctx.ext_error); + mhd_MD5_get_err (&ctx)); exit (99); } -#endif num_failed += check_result (MHD_FUNC_, i, digest, @@ -509,7 +489,7 @@ test2_bin (void) unsigned int num_failed = 0; struct mhd_Md5Ctx ctx; - mhd_MD5_init_one_time (&ctx); + mhd_MD5_init (&ctx); for (i = 0; i < units2_num; i++) { @@ -524,15 +504,13 @@ test2_bin (void) data_units2[i].bin_l.bin + part_s); mhd_MD5_finish_reset (&ctx, digest); -#if defined(MHD_MD5_EXTR) if (mhd_MD5_has_err (&ctx)) { fprintf (stderr, "External hashing error: %d.\n", - ctx.ext_error); + mhd_MD5_get_err (&ctx)); exit (99); } -#endif num_failed += check_result (MHD_FUNC_, i, digest, @@ -563,7 +541,7 @@ test_unaligned (void) || (NULL == digest_buf)) exit (99); - mhd_MD5_init_one_time (&ctx); + mhd_MD5_init (&ctx); for (offset = MAX_OFFSET; offset >= 1; --offset) { @@ -583,15 +561,13 @@ test_unaligned (void) unaligned_buf); mhd_MD5_finish_reset (&ctx, unaligned_digest); -#if defined(MHD_MD5_EXTR) if (mhd_MD5_has_err (&ctx)) { fprintf (stderr, "External hashing error: %d.\n", - ctx.ext_error); + mhd_MD5_get_err (&ctx)); exit (99); } -#endif num_failed += check_result (MHD_FUNC_, MAX_OFFSET - offset, unaligned_digest, @@ -687,13 +663,13 @@ main (int argc, "93b885adfe0da089cdf634904fd59f71" }, { "Two bytes 0x00 0x00", "0000", - "c4103f122d27677c9db144cae1394a66" }, - { NULL, NULL, NULL } + "c4103f122d27677c9db144cae1394a66" } }; struct mhd_Md5Ctx ctx; uint8_t digest[mhd_MD5_DIGEST_SIZE]; uint8_t data[1024]; size_t data_len; + const char *test_name; unsigned int passed = 0; unsigned int total = 0; unsigned int num_failed; @@ -701,11 +677,11 @@ main (int argc, if (mhdt_has_param (argc, argv, "-v") || mhdt_has_param (argc, argv, "--verbose")) verbose = 1; - while (NULL != tests[total].name) + for (total = 0; total < sizeof(tests) / sizeof(tests[0]); ++total) { const struct Test *t = &tests[total]; - mhd_MD5_init_one_time (&ctx); + mhd_MD5_init (&ctx); if (!mhd_MD5_has_err (&ctx)) { data_len = hex2bin (t->input, @@ -727,24 +703,26 @@ main (int argc, } else { - printf ("FAIL: %s - error in finish\n", + printf ("FAIL: %s - digest function failed due to backend error\n", t->name); + exit (99); } } else { printf ("FAIL: %s - error in init\n", t->name); + exit (99); } mhd_MD5_deinit (&ctx); - total++; } /* * Test update functionality */ total++; - mhd_MD5_init_one_time (&ctx); + test_name = "Multi-update: a + b + c"; + mhd_MD5_init (&ctx); if (!mhd_MD5_has_err (&ctx)) { data[0] = 'a'; @@ -766,9 +744,21 @@ main (int argc, if (check_digest (digest, mhd_MD5_DIGEST_SIZE, "900150983cd24fb0d6963f7d28e17f72", - "Multi-update: a + b + c")) + test_name)) passed++; } + else + { + printf ("FAIL: %s - backend reported error\n", + test_name); + exit (99); + } + } + else + { + printf ("FAIL: %s - backend reported initialisation error\n", + test_name); + exit (99); } mhd_MD5_deinit (&ctx); @@ -776,7 +766,8 @@ main (int argc, * Tests finish_reset and reuse of context */ total++; - mhd_MD5_init_one_time (&ctx); + test_name = "Reset and reuse context"; + mhd_MD5_init (&ctx); if (!mhd_MD5_has_err (&ctx)) { /* First hash */ @@ -803,9 +794,21 @@ main (int argc, if (check_digest (digest, mhd_MD5_DIGEST_SIZE, "900150983cd24fb0d6963f7d28e17f72", - "Reset and reuse context")) + test_name)) passed++; } + else + { + printf ("FAIL: %s - backend reported error\n", + test_name); + exit (99); + } + } + else + { + printf ("FAIL: %s - backend reported initialisation error\n", + test_name); + exit (99); } mhd_MD5_deinit (&ctx);