/* * 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. */ /* Based on OpenBSD modifications. * Optimized by Karlson2k (Evgeny Grin). */ #include "md5.h" #include #ifdef HAVE_MEMORY_H #include #endif /* HAVE_MEMORY_H */ #include "mhd_bithelpers.h" #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. * * @param ctx_ must be a `struct MD5Context *` */ void MHD_MD5Init (void *ctx_) { struct MD5Context *ctx = ctx_; mhd_assert (ctx != NULL); 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); } static void MD5Transform (uint32_t state[4], const uint8_t block[MD5_BLOCK_SIZE]); /** * Final wrapup--call MD5Pad, fill in digest and zero out ctx. * * @param ctx_ must be a `struct MD5Context *` */ void MHD_MD5Final (void *ctx_, uint8_t digest[MD5_DIGEST_SIZE]) { struct MD5Context *ctx = ctx_; uint64_t count_bits; size_t have_bytes; mhd_assert (ctx != NULL); mhd_assert (digest != NULL); /* Convert count to 8 bytes in little endian order. */ have_bytes = (ctx->count) & (MD5_BLOCK_SIZE - 1); /* Pad data */ /* Buffer always have space for one byte or more. */ ctx->buffer[have_bytes++] = 0x80; /* First padding byte is 0x80 */ 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) { 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); } 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]); } /* Erase buffer */ memset (ctx, 0, sizeof(*ctx)); } /* 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. */ 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; } /** * Update context to reflect the concatenation of another buffer full * 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 */ void MHD_MD5Update (void *ctx_, const uint8_t *input, size_t len) { struct MD5Context *ctx = ctx_; 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; if (len >= need) { 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; } } /* Handle any remaining bytes of data. */ if (0 != len) memcpy (ctx->buffer + have, input, len); } /* end of md5.c */