libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 532debba7cb1964cebcf6b352c1a5be7f8af640f
parent 98a03752605321c60c3c6d08506736d8c25ab594
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Tue, 13 Sep 2022 19:17:28 +0300

md5: replaced public domain MD5 implementation with our own implementation

Diffstat:
Msrc/microhttpd/digestauth.c | 8++++----
Msrc/microhttpd/md5.c | 624+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/microhttpd/md5.h | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/microhttpd/test_md5.c | 56++++++++++++++++++++++++++++----------------------------
4 files changed, 485 insertions(+), 340 deletions(-)

diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c @@ -304,7 +304,7 @@ MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3) union DigestCtx { #ifdef MHD_MD5_SUPPORT - struct MD5Context md5_ctx; + struct Md5Ctx md5_ctx; #endif /* MHD_MD5_SUPPORT */ #ifdef MHD_SHA256_SUPPORT struct Sha256Ctx sha256_ctx; @@ -419,7 +419,7 @@ digest_init (struct DigestAlgorithm *da) #ifdef MHD_MD5_SUPPORT if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) { - MHD_MD5Init (&da->ctx.md5_ctx); + MHD_MD5_init (&da->ctx.md5_ctx); #ifdef _DEBUG da->inited = true; #endif @@ -470,7 +470,7 @@ digest_update (struct DigestAlgorithm *da, mhd_assert (! da->digest_calculated); #ifdef MHD_MD5_SUPPORT if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) - MHD_MD5Update (&da->ctx.md5_ctx, (const uint8_t *) data, length); + MHD_MD5_update (&da->ctx.md5_ctx, (const uint8_t *) data, length); else #endif /* MHD_MD5_SUPPORT */ #ifdef MHD_SHA256_SUPPORT @@ -528,7 +528,7 @@ digest_calc_hash (struct DigestAlgorithm *da, uint8_t *digest) mhd_assert (! da->digest_calculated); #ifdef MHD_MD5_SUPPORT if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) - MHD_MD5Final (&da->ctx.md5_ctx, digest); + MHD_MD5_finish (&da->ctx.md5_ctx, digest); else #endif /* MHD_MD5_SUPPORT */ #ifdef MHD_SHA256_SUPPORT diff --git a/src/microhttpd/md5.c b/src/microhttpd/md5.c @@ -1,24 +1,30 @@ /* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MHD_MD5Init, call MHD_MD5Update as - * needed on buffers full of bytes, and then call MHD_MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ + This file is part of GNU libmicrohttpd + Copyright (C) 2022 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. -/* Based on OpenBSD modifications. - * Optimized by Karlson2k (Evgeny Grin). */ + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. + If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file microhttpd/md5.c + * @brief Calculation of MD5 digest as defined in RFC 1321 + * @author Karlson2k (Evgeny Grin) + */ #include "md5.h" + #include <string.h> #ifdef HAVE_MEMORY_H #include <memory.h> @@ -27,293 +33,379 @@ #include "mhd_assert.h" /** - * Number of bytes in single MD5 word - * used to process data - */ -#define MD5_BYTES_IN_WORD (32 / 8) - - -/** - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. + * Initialise structure for MD5 calculation. * - * @param ctx must be a `struct MD5Context *` + * @param ctx the calculation context */ void -MHD_MD5Init (struct MD5Context *ctx) +MHD_MD5_init (struct Md5Ctx *ctx) { - mhd_assert (ctx != NULL); + /* 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; - ctx->state[0] = UINT32_C (0x67452301); - ctx->state[1] = UINT32_C (0xefcdab89); - ctx->state[2] = UINT32_C (0x98badcfe); - ctx->state[3] = UINT32_C (0x10325476); } +/** + * Base of MD5 transformation. + * Gets full 64 bytes block of data and updates hash values; + * @param H hash values + * @param M the data buffer with #MD5_BLOCK_SIZE bytes block + */ static void -MD5Transform (uint32_t state[4], - const uint8_t block[MD5_BLOCK_SIZE]); +md5_transform (uint32_t H[MD5_HASH_SIZE_WORDS], + const void *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. */ + /* The input data is loaded in correct format before + calculations on each step. */ +#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)) + + /* Round 1. */ + + 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)); + + /* 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) + + /* 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)); + + /* 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) + + /* 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)); + + /* 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) + + /* 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)); + + /* 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; +} /** - * Final wrapup--call MD5Pad, fill in digest and zero out ctx. + * Process portion of bytes. * - * @param ctx must be a `struct MD5Context *` + * @param ctx the calculation context + * @param data bytes to add to hash + * @param length number of bytes in @a data */ void -MHD_MD5Final (struct MD5Context *ctx, - uint8_t digest[MD5_DIGEST_SIZE]) +MHD_MD5_update (struct Md5Ctx *ctx, + const uint8_t *data, + size_t length) { - uint64_t count_bits; - size_t have_bytes; + unsigned int bytes_have; /**< Number of bytes in the context buffer */ - mhd_assert (ctx != NULL); - mhd_assert (digest != NULL); + mhd_assert ((data != NULL) || (length == 0)); - /* Convert count to 8 bytes in little endian order. */ - have_bytes = (ctx->count) & (MD5_BLOCK_SIZE - 1); +#ifndef MHD_FAVOR_SMALL_CODE + if (0 == length) + return; /* Shortcut, do nothing */ +#endif /* MHD_FAVOR_SMALL_CODE */ - /* Pad data */ - /* Buffer always have space for one byte or more. */ - ctx->buffer[have_bytes++] = 0x80; /* First padding byte is 0x80 */ + /* Note: (count & (MD5_BLOCK_SIZE-1)) + equals (count % MD5_BLOCK_SIZE) for this block size. */ + bytes_have = (unsigned int) (ctx->count & (MD5_BLOCK_SIZE - 1)); + ctx->count += length; - if (MD5_BLOCK_SIZE - have_bytes < 8) - { /* Not enough space to put number of bits */ - while (have_bytes < MD5_BLOCK_SIZE) - ctx->buffer[have_bytes++] = 0; - MD5Transform (ctx->state, ctx->buffer); - have_bytes = 0; /* Additional block */ - } - /* Pad out to 56 */ - memset (ctx->buffer + have_bytes, 0, MD5_BLOCK_SIZE - have_bytes - 8); - - /* Put number of bits */ - count_bits = ctx->count << 3; - _MHD_PUT_64BIT_LE_SAFE (ctx->buffer + 56, count_bits); - MD5Transform (ctx->state, ctx->buffer); - - /* Put digest in LE mode */ -#ifndef _MHD_PUT_32BIT_LE_UNALIGNED - if (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN) + if (0 != bytes_have) { - uint32_t alig_dgst[MD5_DIGEST_SIZE / MD5_BYTES_IN_WORD]; - _MHD_PUT_32BIT_LE (alig_dgst + 0, ctx->state[0]); - _MHD_PUT_32BIT_LE (alig_dgst + 1, ctx->state[1]); - _MHD_PUT_32BIT_LE (alig_dgst + 2, ctx->state[2]); - _MHD_PUT_32BIT_LE (alig_dgst + 3, ctx->state[3]); - /* Copy result to unaligned destination address */ - memcpy (digest, alig_dgst, MD5_DIGEST_SIZE); + unsigned int bytes_left = MD5_BLOCK_SIZE - bytes_have; + if (length >= bytes_left) + { /* Combine new data with data in the buffer and + process the full block. */ + memcpy (((uint8_t *) ctx->buffer) + bytes_have, + data, + bytes_left); + data += bytes_left; + length -= bytes_left; + md5_transform (ctx->H, ctx->buffer); + bytes_have = 0; + } } - else -#else /* _MHD_PUT_32BIT_LE_UNALIGNED */ - if (1) -#endif /* _MHD_PUT_32BIT_LE_UNALIGNED */ - { - /* 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), ctx->state[0]); - _MHD_PUT_32BIT_LE ((void *) (digest + 4), ctx->state[1]); - _MHD_PUT_32BIT_LE ((void *) (digest + 8), ctx->state[2]); - _MHD_PUT_32BIT_LE ((void *) (digest + 12), ctx->state[3]); + + while (MD5_BLOCK_SIZE <= length) + { /* Process any full blocks of new data directly, + without copying to the buffer. */ + md5_transform (ctx->H, data); + data += MD5_BLOCK_SIZE; + length -= MD5_BLOCK_SIZE; } - /* Erase buffer */ - memset (ctx, 0, sizeof(*ctx)); + if (0 != length) + { /* Copy incomplete block of new data (if any) + to the buffer. */ + memcpy (((uint8_t *) ctx->buffer) + bytes_have, data, length); + } } -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1 (z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ - (w += f (x, y, z) + data, w = _MHD_ROTL32(w, s), w += x) - /** - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MHD_MD5Update blocks - * the data and converts bytes into longwords for this routine. + * Size of "length" insertion in bits. + * See RFC 1321, end of Clause 3.2 (step 2). */ -static void -MD5Transform (uint32_t state[4], - const uint8_t block[MD5_BLOCK_SIZE]) -{ - uint32_t a, b, c, d; - uint32_t data_buf[MD5_BLOCK_SIZE / MD5_BYTES_IN_WORD]; - const uint32_t *in; - -#if (_MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN) || \ - ! defined(_MHD_GET_32BIT_LE_UNALIGNED) - if (0 != (((uintptr_t) block) % _MHD_UINT32_ALIGN)) - { - /* Copy data to the aligned buffer */ - memcpy (data_buf, block, MD5_BLOCK_SIZE); - in = data_buf; - } - else - in = (const uint32_t *) ((const void *) block); -#endif /* _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN) || \ - ! _MHD_GET_32BIT_LE_UNALIGNED */ -#if _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN - data_buf[0] = _MHD_GET_32BIT_LE (in + 0); - data_buf[1] = _MHD_GET_32BIT_LE (in + 1); - data_buf[2] = _MHD_GET_32BIT_LE (in + 2); - data_buf[3] = _MHD_GET_32BIT_LE (in + 3); - data_buf[4] = _MHD_GET_32BIT_LE (in + 4); - data_buf[5] = _MHD_GET_32BIT_LE (in + 5); - data_buf[6] = _MHD_GET_32BIT_LE (in + 6); - data_buf[7] = _MHD_GET_32BIT_LE (in + 7); - data_buf[8] = _MHD_GET_32BIT_LE (in + 8); - data_buf[9] = _MHD_GET_32BIT_LE (in + 9); - data_buf[10] = _MHD_GET_32BIT_LE (in + 10); - data_buf[11] = _MHD_GET_32BIT_LE (in + 11); - data_buf[12] = _MHD_GET_32BIT_LE (in + 12); - data_buf[13] = _MHD_GET_32BIT_LE (in + 13); - data_buf[14] = _MHD_GET_32BIT_LE (in + 14); - data_buf[15] = _MHD_GET_32BIT_LE (in + 15); - in = data_buf; -#endif /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */ - - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - - MD5STEP (F1, a, b, c, d, in[0] + UINT32_C (0xd76aa478), 7); - MD5STEP (F1, d, a, b, c, in[1] + UINT32_C (0xe8c7b756), 12); - MD5STEP (F1, c, d, a, b, in[2] + UINT32_C (0x242070db), 17); - MD5STEP (F1, b, c, d, a, in[3] + UINT32_C (0xc1bdceee), 22); - MD5STEP (F1, a, b, c, d, in[4] + UINT32_C (0xf57c0faf), 7); - MD5STEP (F1, d, a, b, c, in[5] + UINT32_C (0x4787c62a), 12); - MD5STEP (F1, c, d, a, b, in[6] + UINT32_C (0xa8304613), 17); - MD5STEP (F1, b, c, d, a, in[7] + UINT32_C (0xfd469501), 22); - MD5STEP (F1, a, b, c, d, in[8] + UINT32_C (0x698098d8), 7); - MD5STEP (F1, d, a, b, c, in[9] + UINT32_C (0x8b44f7af), 12); - MD5STEP (F1, c, d, a, b, in[10] + UINT32_C (0xffff5bb1), 17); - MD5STEP (F1, b, c, d, a, in[11] + UINT32_C (0x895cd7be), 22); - MD5STEP (F1, a, b, c, d, in[12] + UINT32_C (0x6b901122), 7); - MD5STEP (F1, d, a, b, c, in[13] + UINT32_C (0xfd987193), 12); - MD5STEP (F1, c, d, a, b, in[14] + UINT32_C (0xa679438e), 17); - MD5STEP (F1, b, c, d, a, in[15] + UINT32_C (0x49b40821), 22); - - MD5STEP (F2, a, b, c, d, in[1] + UINT32_C (0xf61e2562), 5); - MD5STEP (F2, d, a, b, c, in[6] + UINT32_C (0xc040b340), 9); - MD5STEP (F2, c, d, a, b, in[11] + UINT32_C (0x265e5a51), 14); - MD5STEP (F2, b, c, d, a, in[0] + UINT32_C (0xe9b6c7aa), 20); - MD5STEP (F2, a, b, c, d, in[5] + UINT32_C (0xd62f105d), 5); - MD5STEP (F2, d, a, b, c, in[10] + UINT32_C (0x02441453), 9); - MD5STEP (F2, c, d, a, b, in[15] + UINT32_C (0xd8a1e681), 14); - MD5STEP (F2, b, c, d, a, in[4] + UINT32_C (0xe7d3fbc8), 20); - MD5STEP (F2, a, b, c, d, in[9] + UINT32_C (0x21e1cde6), 5); - MD5STEP (F2, d, a, b, c, in[14] + UINT32_C (0xc33707d6), 9); - MD5STEP (F2, c, d, a, b, in[3] + UINT32_C (0xf4d50d87), 14); - MD5STEP (F2, b, c, d, a, in[8] + UINT32_C (0x455a14ed), 20); - MD5STEP (F2, a, b, c, d, in[13] + UINT32_C (0xa9e3e905), 5); - MD5STEP (F2, d, a, b, c, in[2] + UINT32_C (0xfcefa3f8), 9); - MD5STEP (F2, c, d, a, b, in[7] + UINT32_C (0x676f02d9), 14); - MD5STEP (F2, b, c, d, a, in[12] + UINT32_C (0x8d2a4c8a), 20); - - MD5STEP (F3, a, b, c, d, in[5] + UINT32_C (0xfffa3942), 4); - MD5STEP (F3, d, a, b, c, in[8] + UINT32_C (0x8771f681), 11); - MD5STEP (F3, c, d, a, b, in[11] + UINT32_C (0x6d9d6122), 16); - MD5STEP (F3, b, c, d, a, in[14] + UINT32_C (0xfde5380c), 23); - MD5STEP (F3, a, b, c, d, in[1] + UINT32_C (0xa4beea44), 4); - MD5STEP (F3, d, a, b, c, in[4] + UINT32_C (0x4bdecfa9), 11); - MD5STEP (F3, c, d, a, b, in[7] + UINT32_C (0xf6bb4b60), 16); - MD5STEP (F3, b, c, d, a, in[10] + UINT32_C (0xbebfbc70), 23); - MD5STEP (F3, a, b, c, d, in[13] + UINT32_C (0x289b7ec6), 4); - MD5STEP (F3, d, a, b, c, in[0] + UINT32_C (0xeaa127fa), 11); - MD5STEP (F3, c, d, a, b, in[3] + UINT32_C (0xd4ef3085), 16); - MD5STEP (F3, b, c, d, a, in[6] + UINT32_C (0x04881d05), 23); - MD5STEP (F3, a, b, c, d, in[9] + UINT32_C (0xd9d4d039), 4); - MD5STEP (F3, d, a, b, c, in[12] + UINT32_C (0xe6db99e5), 11); - MD5STEP (F3, c, d, a, b, in[15] + UINT32_C (0x1fa27cf8), 16); - MD5STEP (F3, b, c, d, a, in[2] + UINT32_C (0xc4ac5665), 23); - - MD5STEP (F4, a, b, c, d, in[0] + UINT32_C (0xf4292244), 6); - MD5STEP (F4, d, a, b, c, in[7] + UINT32_C (0x432aff97), 10); - MD5STEP (F4, c, d, a, b, in[14] + UINT32_C (0xab9423a7), 15); - MD5STEP (F4, b, c, d, a, in[5] + UINT32_C (0xfc93a039), 21); - MD5STEP (F4, a, b, c, d, in[12] + UINT32_C (0x655b59c3), 6); - MD5STEP (F4, d, a, b, c, in[3] + UINT32_C (0x8f0ccc92), 10); - MD5STEP (F4, c, d, a, b, in[10] + UINT32_C (0xffeff47d), 15); - MD5STEP (F4, b, c, d, a, in[1] + UINT32_C (0x85845dd1), 21); - MD5STEP (F4, a, b, c, d, in[8] + UINT32_C (0x6fa87e4f), 6); - MD5STEP (F4, d, a, b, c, in[15] + UINT32_C (0xfe2ce6e0), 10); - MD5STEP (F4, c, d, a, b, in[6] + UINT32_C (0xa3014314), 15); - MD5STEP (F4, b, c, d, a, in[13] + UINT32_C (0x4e0811a1), 21); - MD5STEP (F4, a, b, c, d, in[4] + UINT32_C (0xf7537e82), 6); - MD5STEP (F4, d, a, b, c, in[11] + UINT32_C (0xbd3af235), 10); - MD5STEP (F4, c, d, a, b, in[2] + UINT32_C (0x2ad7d2bb), 15); - MD5STEP (F4, b, c, d, a, in[9] + UINT32_C (0xeb86d391), 21); - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; -} +#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) /** - * Update context to reflect the concatenation of another buffer full - * of bytes. + * Finalise MD5 calculation, return digest. * - * @param ctx must be a `struct MD5Context *` - * @param input bytes to add to hash - * @param len the number of bytes in @a data + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #MD5_DIGEST_SIZE bytes */ void -MHD_MD5Update (struct MD5Context *ctx, - const uint8_t *input, - size_t len) +MHD_MD5_finish (struct Md5Ctx *ctx, + uint8_t digest[MD5_DIGEST_SIZE]) { - size_t have, need; - - mhd_assert (ctx != NULL); - mhd_assert ((ctx != NULL) || (len == 0)); - - /* Check how many bytes we already have and how many more we need. */ - have = (size_t) ((ctx->count) & (MD5_BLOCK_SIZE - 1)); - need = MD5_BLOCK_SIZE - have; - - /* Update bytecount */ - ctx->count += (uint64_t) len; + 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 & (MD5_BLOCK_SIZE-1)) + equals (count % MD5_BLOCK_SIZE) for this block size. */ + bytes_have = (unsigned int) (ctx->count & (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 (MD5_BLOCK_SIZE - bytes_have < MD5_SIZE_OF_LEN_ADD) + { /* No space in the current block to put the total length of message. + Pad the current block with zeros and process it. */ + if (bytes_have < MD5_BLOCK_SIZE) + memset (((uint8_t *) ctx->buffer) + bytes_have, 0, + MD5_BLOCK_SIZE - bytes_have); + /* Process the full block. */ + md5_transform (ctx->H, ctx->buffer); + /* Start the new block. */ + bytes_have = 0; + } - if (len >= need) + /* Pad the rest of the buffer with zeros. */ + memset (((uint8_t *) ctx->buffer) + bytes_have, 0, + MD5_BLOCK_SIZE - MD5_SIZE_OF_LEN_ADD - bytes_have); + /* Put the number of bits in processed data as little-endian value. + See RFC 1321, clauses 2 and 3.2 (step 2). */ + _MHD_PUT_64BIT_LE_SAFE (ctx->buffer + MD5_BLOCK_SIZE_WORDS - 2, + num_bits); + /* Process the full final block. */ + md5_transform (ctx->H, ctx->buffer); + + /* Put in LE mode the hash as the final digest. + See RFC 1321, clauses 2 and 3.5 (step 5). */ +#ifndef _MHD_PUT_32BIT_LE_UNALIGNED + if (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN) + { /* The destination is unaligned */ + uint32_t alig_dgst[MD5_DIGEST_SIZE_WORDS]; + _MHD_PUT_32BIT_LE (alig_dgst + 0, ctx->H[0]); + _MHD_PUT_32BIT_LE (alig_dgst + 1, ctx->H[1]); + _MHD_PUT_32BIT_LE (alig_dgst + 2, ctx->H[2]); + _MHD_PUT_32BIT_LE (alig_dgst + 3, ctx->H[3]); + /* Copy result to the unaligned destination address. */ + memcpy (digest, alig_dgst, MD5_DIGEST_SIZE); + } + else /* Combined with the next 'if' */ +#endif /* ! _MHD_PUT_32BIT_LE_UNALIGNED */ + if (1) { - if (have != 0) - { - memcpy (ctx->buffer + have, - input, - need); - MD5Transform (ctx->state, ctx->buffer); - input += need; - len -= need; - have = 0; - } - - /* Process data in MD5_BLOCK_SIZE-byte chunks. */ - while (len >= MD5_BLOCK_SIZE) - { - MD5Transform (ctx->state, - (const unsigned char *) input); - input += MD5_BLOCK_SIZE; - len -= MD5_BLOCK_SIZE; - } + /* Use cast to (void*) here to mute compiler alignment warnings. + * Compilers are not smart enough to see that alignment has been checked. */ + _MHD_PUT_32BIT_LE ((void *) (digest + 0 * MD5_BYTES_IN_WORD), ctx->H[0]); + _MHD_PUT_32BIT_LE ((void *) (digest + 1 * MD5_BYTES_IN_WORD), ctx->H[1]); + _MHD_PUT_32BIT_LE ((void *) (digest + 2 * MD5_BYTES_IN_WORD), ctx->H[2]); + _MHD_PUT_32BIT_LE ((void *) (digest + 3 * MD5_BYTES_IN_WORD), ctx->H[3]); } - /* Handle any remaining bytes of data. */ - if (0 != len) - memcpy (ctx->buffer + have, - input, - len); + /* Erase potentially sensitive data. */ + memset (ctx, 0, sizeof(struct Md5Ctx)); } - - -/* end of md5.c */ diff --git a/src/microhttpd/md5.h b/src/microhttpd/md5.h @@ -1,73 +1,126 @@ /* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MHD_MD5Init, call MHD_MD5Update as - * needed on buffers full of bytes, and then call MHD_MD5Final, which - * will fill a supplied 16-byte array with the digest. + This file is part of GNU libmicrohttpd + Copyright (C) 2022 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. + If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * @file microhttpd/md5.h + * @brief Calculation of MD5 digest + * @author Karlson2k (Evgeny Grin) */ #ifndef MHD_MD5_H -#define MHD_MD5_H +#define MHD_MD5_H 1 #include "mhd_options.h" #include <stdint.h> #ifdef HAVE_STDDEF_H -#include <stddef.h> +#include <stddef.h> /* for size_t */ #endif /* HAVE_STDDEF_H */ -#define MD5_BLOCK_SIZE 64 -#define MD5_DIGEST_SIZE 16 -#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_SIZE * 2 + 1) +/** + * Number of bits in single MD5 word. + */ +#define MD5_WORD_SIZE_BITS 32 + +/** + * Number of bytes in single MD5 word. + */ +#define MD5_BYTES_IN_WORD (MD5_WORD_SIZE_BITS / 8) + +/** + * Hash is kept internally as four 32-bit words. + * This is intermediate hash size, used during computing the final digest. + */ +#define MD5_HASH_SIZE_WORDS 4 + +/** + * Size of MD5 resulting digest in bytes. + * This is the final digest size, not intermediate hash. + */ +#define MD5_DIGEST_SIZE_WORDS MD5_HASH_SIZE_WORDS + +/** + * Size of MD5 resulting digest in bytes + * This is the final digest size, not intermediate hash. + */ +#define MD5_DIGEST_SIZE (MD5_DIGEST_SIZE_WORDS * MD5_BYTES_IN_WORD) + +/** + * Size of MD5 digest string in chars including termination NUL. + */ +#define MD5_DIGEST_STRING_SIZE ((MD5_DIGEST_SIZE) * 2 + 1) -struct MD5Context +/** + * Size of MD5 single processing block in bits. + */ +#define MD5_BLOCK_SIZE_BITS 512 + +/** + * Size of MD5 single processing block in bytes. + */ +#define MD5_BLOCK_SIZE (MD5_BLOCK_SIZE_BITS / 8) + +/** + * Size of MD5 single processing block in words. + */ +#define MD5_BLOCK_SIZE_WORDS (MD5_BLOCK_SIZE_BITS / MD5_WORD_SIZE_BITS) + + +/** + * MD5 calculation context + */ +struct Md5Ctx { - uint32_t state[4]; /* state */ - uint64_t count; /* number of bytes, mod 2^64 */ - uint8_t buffer[MD5_BLOCK_SIZE]; /* input buffer */ + uint32_t H[MD5_HASH_SIZE_WORDS]; /**< Intermediate hash value / digest at end of calculation */ + uint32_t buffer[MD5_BLOCK_SIZE_WORDS]; /**< MD5 input data buffer */ + uint64_t count; /**< number of bytes, mod 2^64 */ }; - /** - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. + * Initialise structure for MD5 calculation. * - * @param ctx must be a `struct MD5Context *` + * @param ctx the calculation context */ void -MHD_MD5Init (struct MD5Context *ctx); +MHD_MD5_init (struct Md5Ctx *ctx); /** - * Update context to reflect the concatenation of another buffer full - * of bytes. + * MD5 process portion of bytes. * - * @param ctx must be a `struct MD5Context *` - * @param input bytes to add to hash - * @param len the number of bytes in @a data + * @param ctx the calculation context + * @param data bytes to add to hash + * @param length number of bytes in @a data */ void -MHD_MD5Update (struct MD5Context *ctx, - const uint8_t *input, - size_t len); +MHD_MD5_update (struct Md5Ctx *ctx, + const uint8_t *data, + size_t length); /** - * Final wrapup--call MD5Pad, fill in digest and zero out ctx. + * Finalise MD5 calculation, return digest. * - * @param ctx must be a `struct MD5Context *` + * @param ctx the calculation context + * @param[out] digest set to the hash, must be #MD5_DIGEST_SIZE bytes */ void -MHD_MD5Final (struct MD5Context *ctx, - uint8_t digest[MD5_DIGEST_SIZE]); - +MHD_MD5_finish (struct Md5Ctx *ctx, + uint8_t digest[MD5_DIGEST_SIZE]); -#endif /* !MHD_MD5_H */ +#endif /* MHD_MD5_H */ diff --git a/src/microhttpd/test_md5.c b/src/microhttpd/test_md5.c @@ -251,8 +251,8 @@ check_result (const char *test_name, check_num++; /* Print 1-based numbers */ if (failed) { - char calc_str[MD5_DIGEST_STRING_LENGTH]; - char expc_str[MD5_DIGEST_STRING_LENGTH]; + char calc_str[MD5_DIGEST_STRING_SIZE]; + char expc_str[MD5_DIGEST_STRING_SIZE]; bin2hex (calculated, MD5_DIGEST_SIZE, calc_str); bin2hex (expected, MD5_DIGEST_SIZE, expc_str); fprintf (stderr, @@ -262,7 +262,7 @@ check_result (const char *test_name, } else if (verbose) { - char calc_str[MD5_DIGEST_STRING_LENGTH]; + char calc_str[MD5_DIGEST_STRING_SIZE]; bin2hex (calculated, MD5_DIGEST_SIZE, calc_str); printf ( "PASSED: %s check %u: calculated digest %s match expected digest.\n", @@ -286,13 +286,13 @@ test1_str (void) for (i = 0; i < units1_num; i++) { - struct MD5Context ctx; + struct Md5Ctx ctx; uint8_t digest[MD5_DIGEST_SIZE]; - MHD_MD5Init (&ctx); - MHD_MD5Update (&ctx, (const uint8_t*) data_units1[i].str_l.str, - data_units1[i].str_l.len); - MHD_MD5Final (&ctx, digest); + MHD_MD5_init (&ctx); + MHD_MD5_update (&ctx, (const uint8_t *) data_units1[i].str_l.str, + data_units1[i].str_l.len); + MHD_MD5_finish (&ctx, digest); num_failed += check_result (__FUNCTION__, i, digest, data_units1[i].digest); } @@ -308,12 +308,12 @@ test1_bin (void) for (i = 0; i < units2_num; i++) { - struct MD5Context ctx; + struct Md5Ctx ctx; uint8_t digest[MD5_DIGEST_SIZE]; - MHD_MD5Init (&ctx); - MHD_MD5Update (&ctx, data_units2[i].bin_l.bin, data_units2[i].bin_l.len); - MHD_MD5Final (&ctx, digest); + MHD_MD5_init (&ctx); + MHD_MD5_update (&ctx, data_units2[i].bin_l.bin, data_units2[i].bin_l.len); + MHD_MD5_finish (&ctx, digest); num_failed += check_result (__FUNCTION__, i, digest, data_units2[i].digest); } @@ -330,15 +330,15 @@ test2_str (void) for (i = 0; i < units1_num; i++) { - struct MD5Context ctx; + struct Md5Ctx ctx; uint8_t digest[MD5_DIGEST_SIZE]; size_t part_s = data_units1[i].str_l.len / 4; - MHD_MD5Init (&ctx); - MHD_MD5Update (&ctx, (const uint8_t*) data_units1[i].str_l.str, part_s); - MHD_MD5Update (&ctx, (const uint8_t*) data_units1[i].str_l.str + part_s, - data_units1[i].str_l.len - part_s); - MHD_MD5Final (&ctx, digest); + MHD_MD5_init (&ctx); + MHD_MD5_update (&ctx, (const uint8_t *) data_units1[i].str_l.str, part_s); + MHD_MD5_update (&ctx, (const uint8_t *) data_units1[i].str_l.str + part_s, + data_units1[i].str_l.len - part_s); + MHD_MD5_finish (&ctx, digest); num_failed += check_result (__FUNCTION__, i, digest, data_units1[i].digest); } @@ -354,15 +354,15 @@ test2_bin (void) for (i = 0; i < units2_num; i++) { - struct MD5Context ctx; + struct Md5Ctx ctx; uint8_t digest[MD5_DIGEST_SIZE]; size_t part_s = data_units2[i].bin_l.len * 2 / 3; - MHD_MD5Init (&ctx); - MHD_MD5Update (&ctx, data_units2[i].bin_l.bin, part_s); - MHD_MD5Update (&ctx, data_units2[i].bin_l.bin + part_s, - data_units2[i].bin_l.len - part_s); - MHD_MD5Final (&ctx, digest); + MHD_MD5_init (&ctx); + MHD_MD5_update (&ctx, data_units2[i].bin_l.bin, part_s); + MHD_MD5_update (&ctx, data_units2[i].bin_l.bin + part_s, + data_units2[i].bin_l.len - part_s); + MHD_MD5_finish (&ctx, digest); num_failed += check_result (__FUNCTION__, i, digest, data_units2[i].digest); } @@ -391,7 +391,7 @@ test_unaligned (void) for (offset = MAX_OFFSET; offset >= 1; --offset) { - struct MD5Context ctx; + struct Md5Ctx ctx; uint8_t *unaligned_digest; uint8_t *unaligned_buf; @@ -400,9 +400,9 @@ test_unaligned (void) unaligned_digest = digest_buf + MAX_OFFSET - offset; memset (unaligned_digest, 0, MD5_DIGEST_SIZE); - MHD_MD5Init (&ctx); - MHD_MD5Update (&ctx, unaligned_buf, tdata->bin_l.len); - MHD_MD5Final (&ctx, unaligned_digest); + MHD_MD5_init (&ctx); + MHD_MD5_update (&ctx, unaligned_buf, tdata->bin_l.len); + MHD_MD5_finish (&ctx, unaligned_digest); num_failed += check_result (__FUNCTION__, MAX_OFFSET - offset, unaligned_digest, tdata->digest); }