From f959b8435217a903d0b5ddaa2deaa23af54812ab Mon Sep 17 00:00:00 2001 From: "Evgeny Grin (Karlson2k)" Date: Tue, 18 May 2021 22:17:29 +0300 Subject: Fixed unaligned data processing in SHA-256 and MD5 --- src/microhttpd/md5.c | 79 ++++++++++++++++++++++++++++++----------- src/microhttpd/mhd_bithelpers.h | 32 ++++++++++++++++- src/microhttpd/sha1.c | 2 +- src/microhttpd/sha1.h | 2 +- src/microhttpd/sha256.c | 68 ++++++++++++++++++++++++----------- src/microhttpd/sha256.h | 23 ++++++++++-- 6 files changed, 160 insertions(+), 46 deletions(-) diff --git a/src/microhttpd/md5.c b/src/microhttpd/md5.c index 05a2c9e6..785873b5 100644 --- a/src/microhttpd/md5.c +++ b/src/microhttpd/md5.c @@ -26,6 +26,12 @@ #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 @@ -87,26 +93,37 @@ MHD_MD5Final (void *ctx_, /* Put number of bits */ count_bits = ctx->count << 3; - _MHD_PUT_64BIT_LE (ctx->buffer + 56, count_bits); + _MHD_PUT_64BIT_LE_SAFE (ctx->buffer + 56, count_bits); MD5Transform (ctx->state, ctx->buffer); /* Put digest in LE mode */ - _MHD_PUT_32BIT_LE (digest, ctx->state[0]); - _MHD_PUT_32BIT_LE (digest + 4, ctx->state[1]); - _MHD_PUT_32BIT_LE (digest + 8, ctx->state[2]); - _MHD_PUT_32BIT_LE (digest + 12, ctx->state[3]); +#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 */ + { + _MHD_PUT_32BIT_LE (digest, ctx->state[0]); + _MHD_PUT_32BIT_LE (digest + 4, ctx->state[1]); + _MHD_PUT_32BIT_LE (digest + 8, ctx->state[2]); + _MHD_PUT_32BIT_LE (digest + 12, ctx->state[3]); + } /* Erase buffer */ memset (ctx, 0, sizeof(*ctx)); } -/** - * Number of bytes in single SHA-256 word - * used to process data - */ -#define MD5_BYTES_IN_WORD (32 / 8) - /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ @@ -129,18 +146,40 @@ 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 - const uint32_t *in = (const uint32_t *) block; -#else - uint32_t in[MD5_BLOCK_SIZE / MD5_BYTES_IN_WORD]; - int i; - - for (i = 0; i < MD5_BLOCK_SIZE / MD5_BYTES_IN_WORD; i++) +#if (_MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN) || \ + ! defined (_MHD_GET_32BIT_LE_UNALIGNED) + if (0 != (((uintptr_t) block) % _MHD_UINT32_ALIGN)) { - in[i] = _MHD_GET_32BIT_LE (block + i * MD5_BYTES_IN_WORD); + /* Copy data to the aligned buffer */ + memcpy (data_buf, block, MD5_BLOCK_SIZE); + in = data_buf; } -#endif + else + in = (const uint32_t *) 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]; diff --git a/src/microhttpd/mhd_bithelpers.h b/src/microhttpd/mhd_bithelpers.h index 12e1a569..9bd2df79 100644 --- a/src/microhttpd/mhd_bithelpers.h +++ b/src/microhttpd/mhd_bithelpers.h @@ -99,6 +99,17 @@ * put native-endian 64-bit value64 to addr * in little-endian mode. */ +/* Slow version that works with unaligned addr and with any bytes order */ +#define _MHD_PUT_64BIT_LE_SLOW(addr, value64) do { \ + ((uint8_t*) (addr))[0] = (uint8_t) ((uint64_t) (value64)); \ + ((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 8); \ + ((uint8_t*) (addr))[2] = (uint8_t) (((uint64_t) (value64)) >> 16); \ + ((uint8_t*) (addr))[3] = (uint8_t) (((uint64_t) (value64)) >> 24); \ + ((uint8_t*) (addr))[4] = (uint8_t) (((uint64_t) (value64)) >> 32); \ + ((uint8_t*) (addr))[5] = (uint8_t) (((uint64_t) (value64)) >> 40); \ + ((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 48); \ + ((uint8_t*) (addr))[7] = (uint8_t) (((uint64_t) (value64)) >> 56); \ +} while (0) #if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN #define _MHD_PUT_64BIT_LE(addr, value64) \ ((*(uint64_t*) (addr)) = (uint64_t) (value64)) @@ -117,8 +128,23 @@ ((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 48); \ ((uint8_t*) (addr))[7] = (uint8_t) (((uint64_t) (value64)) >> 56); \ } while (0) +/* Indicate that _MHD_PUT_64BIT_LE does not need aligned pointer */ +#define _MHD_PUT_64BIT_LE_UNALIGNED 1 #endif /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */ +/* Put result safely to unaligned address */ +_MHD_static_inline void +_MHD_PUT_64BIT_LE_SAFE (void *dst, uint64_t value) +{ +#ifndef _MHD_PUT_64BIT_LE_UNALIGNED + if (0 != ((uintptr_t) dst) % (_MHD_UINT64_ALIGN)) + _MHD_PUT_64BIT_LE_SLOW (dst, value); + else +#endif /* ! _MHD_PUT_64BIT_BE_UNALIGNED */ + _MHD_PUT_64BIT_LE (dst, value); +} + + /* _MHD_PUT_32BIT_LE (addr, value32) * put native-endian 32-bit value32 to addr * in little-endian mode. @@ -137,6 +163,8 @@ ((uint8_t*) (addr))[2] = (uint8_t) (((uint32_t) (value32)) >> 16); \ ((uint8_t*) (addr))[3] = (uint8_t) (((uint32_t) (value32)) >> 24); \ } while (0) +/* Indicate that _MHD_PUT_32BIT_LE does not need aligned pointer */ +#define _MHD_PUT_32BIT_LE_UNALIGNED 1 #endif /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */ /* _MHD_GET_32BIT_LE (addr) @@ -156,6 +184,8 @@ | (((uint32_t) (((const uint8_t*) addr)[1])) << 8) \ | (((uint32_t) (((const uint8_t*) addr)[2])) << 16) \ | (((uint32_t) (((const uint8_t*) addr)[3])) << 24) ) +/* Indicate that _MHD_GET_32BIT_LE does not need aligned pointer */ +#define _MHD_GET_32BIT_LE_UNALIGNED 1 #endif /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */ @@ -195,7 +225,7 @@ _MHD_PUT_64BIT_BE_SAFE (void *dst, uint64_t value) if (0 != ((uintptr_t) dst) % (_MHD_UINT64_ALIGN)) _MHD_PUT_64BIT_BE_SLOW (dst, value); else -#endif /* _MHD_BYTE_ORDER_IS_BIG_OR_LITTLE_ENDIAN */ +#endif /* ! _MHD_PUT_64BIT_BE_UNALIGNED */ _MHD_PUT_64BIT_BE (dst, value); } diff --git a/src/microhttpd/sha1.c b/src/microhttpd/sha1.c index 2ed84208..5d4a5b93 100644 --- a/src/microhttpd/sha1.c +++ b/src/microhttpd/sha1.c @@ -109,7 +109,7 @@ sha1_transform (uint32_t H[_SHA1_DIGEST_LENGTH], _MHD_GET_32BIT_BE (((const uint8_t*) (buf)) + (t) * SHA1_BYTES_IN_WORD) #ifndef _MHD_GET_32BIT_BE_UNALIGNED - if (0 != ((size_t) data % _MHD_UINT32_ALIGN)) + if (0 != (((uintptr_t) data) % _MHD_UINT32_ALIGN)) { /* Copy the unaligned input data to the aligned buffer */ memcpy (W, data, SHA1_BLOCK_SIZE); diff --git a/src/microhttpd/sha1.h b/src/microhttpd/sha1.h index 59f00217..3464f268 100644 --- a/src/microhttpd/sha1.h +++ b/src/microhttpd/sha1.h @@ -36,7 +36,7 @@ #define _SHA1_DIGEST_LENGTH 5 /** - * Number of bytes in single SHA-1 word + * Number of bits in single SHA-1 word */ #define SHA1_WORD_SIZE_BITS 32 diff --git a/src/microhttpd/sha256.c b/src/microhttpd/sha256.c index 80c41eb5..df6a1aa3 100644 --- a/src/microhttpd/sha256.c +++ b/src/microhttpd/sha256.c @@ -59,12 +59,6 @@ MHD_SHA256_init (void *ctx_) } -/** - * Number of bytes in single SHA-256 word - * used to process data - */ -#define SHA256_BYTES_IN_WORD 4 - /** * Base of SHA-256 transformation. * Gets full 64 bytes block of data and updates hash values; @@ -126,6 +120,18 @@ sha256_transform (uint32_t H[_SHA256_DIGEST_LENGTH], (vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt)); \ (vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0) +#ifndef _MHD_GET_32BIT_BE_UNALIGNED + if (0 != (((uintptr_t) data) % _MHD_UINT32_ALIGN)) + { + /* Copy the unaligned input data to the aligned buffer */ + memcpy (W, data, SHA256_BLOCK_SIZE); + /* The W[] buffer itself will be used as the source of the data, + * but data will be reloaded in correct bytes order during + * the next steps */ + data = (uint8_t*) W; + } +#endif /* _MHD_GET_32BIT_BE_UNALIGNED */ + /* Get value of W(t) from input data buffer, See FIPS PUB 180-4 paragraph 6.2. Input data must be read in big-endian bytes order, @@ -267,7 +273,7 @@ MHD_SHA256_update (void *ctx_, return; /* Do nothing */ /* Note: (count & (SHA256_BLOCK_SIZE-1)) - equal (count % SHA256_BLOCK_SIZE) for this block size. */ + equals (count % SHA256_BLOCK_SIZE) for this block size. */ bytes_have = (unsigned) (ctx->count & (SHA256_BLOCK_SIZE - 1)); ctx->count += length; @@ -275,7 +281,7 @@ MHD_SHA256_update (void *ctx_, { unsigned bytes_left = SHA256_BLOCK_SIZE - bytes_have; if (length >= bytes_left) - { /* Combine new data with data in buffer and + { /* Combine new data with data in the buffer and process full block. */ memcpy (ctx->buffer + bytes_have, data, @@ -289,7 +295,7 @@ MHD_SHA256_update (void *ctx_, while (SHA256_BLOCK_SIZE <= length) { /* Process any full blocks of new data directly, - without copying to buffer. */ + without copying to the buffer. */ sha256_transform (ctx->H, data); data += SHA256_BLOCK_SIZE; length -= SHA256_BLOCK_SIZE; @@ -297,7 +303,7 @@ MHD_SHA256_update (void *ctx_, if (0 != length) { /* Copy incomplete block of new data (if any) - to buffer. */ + to the buffer. */ memcpy (ctx->buffer + bytes_have, data, length); } } @@ -351,20 +357,42 @@ MHD_SHA256_finish (void *ctx_, memset (ctx->buffer + bytes_have, 0, SHA256_BLOCK_SIZE - SHA256_SIZE_OF_LEN_ADD - bytes_have); /* Put number of bits in processed message as big-endian value. */ - _MHD_PUT_64BIT_BE (ctx->buffer + SHA256_BLOCK_SIZE - SHA256_SIZE_OF_LEN_ADD, - num_bits); + _MHD_PUT_64BIT_BE_SAFE (ctx->buffer + SHA256_BLOCK_SIZE + - SHA256_SIZE_OF_LEN_ADD, + num_bits); /* Process full final block. */ sha256_transform (ctx->H, ctx->buffer); /* Put final hash/digest in BE mode */ - _MHD_PUT_32BIT_BE (digest + 0 * SHA256_BYTES_IN_WORD, ctx->H[0]); - _MHD_PUT_32BIT_BE (digest + 1 * SHA256_BYTES_IN_WORD, ctx->H[1]); - _MHD_PUT_32BIT_BE (digest + 2 * SHA256_BYTES_IN_WORD, ctx->H[2]); - _MHD_PUT_32BIT_BE (digest + 3 * SHA256_BYTES_IN_WORD, ctx->H[3]); - _MHD_PUT_32BIT_BE (digest + 4 * SHA256_BYTES_IN_WORD, ctx->H[4]); - _MHD_PUT_32BIT_BE (digest + 5 * SHA256_BYTES_IN_WORD, ctx->H[5]); - _MHD_PUT_32BIT_BE (digest + 6 * SHA256_BYTES_IN_WORD, ctx->H[6]); - _MHD_PUT_32BIT_BE (digest + 7 * SHA256_BYTES_IN_WORD, ctx->H[7]); +#ifndef _MHD_PUT_32BIT_BE_UNALIGNED + if (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN) + { + uint32_t alig_dgst[_SHA256_DIGEST_LENGTH]; + _MHD_PUT_32BIT_BE (alig_dgst + 0, ctx->H[0]); + _MHD_PUT_32BIT_BE (alig_dgst + 1, ctx->H[1]); + _MHD_PUT_32BIT_BE (alig_dgst + 2, ctx->H[2]); + _MHD_PUT_32BIT_BE (alig_dgst + 3, ctx->H[3]); + _MHD_PUT_32BIT_BE (alig_dgst + 4, ctx->H[4]); + _MHD_PUT_32BIT_BE (alig_dgst + 5, ctx->H[5]); + _MHD_PUT_32BIT_BE (alig_dgst + 6, ctx->H[6]); + _MHD_PUT_32BIT_BE (alig_dgst + 7, ctx->H[7]); + /* Copy result to unaligned destination address */ + memcpy (digest, alig_dgst, SHA256_DIGEST_SIZE); + } + else +#else /* _MHD_PUT_32BIT_BE_UNALIGNED */ + if (1) +#endif /* _MHD_PUT_32BIT_BE_UNALIGNED */ + { + _MHD_PUT_32BIT_BE (digest + 0 * SHA256_BYTES_IN_WORD, ctx->H[0]); + _MHD_PUT_32BIT_BE (digest + 1 * SHA256_BYTES_IN_WORD, ctx->H[1]); + _MHD_PUT_32BIT_BE (digest + 2 * SHA256_BYTES_IN_WORD, ctx->H[2]); + _MHD_PUT_32BIT_BE (digest + 3 * SHA256_BYTES_IN_WORD, ctx->H[3]); + _MHD_PUT_32BIT_BE (digest + 4 * SHA256_BYTES_IN_WORD, ctx->H[4]); + _MHD_PUT_32BIT_BE (digest + 5 * SHA256_BYTES_IN_WORD, ctx->H[5]); + _MHD_PUT_32BIT_BE (digest + 6 * SHA256_BYTES_IN_WORD, ctx->H[6]); + _MHD_PUT_32BIT_BE (digest + 7 * SHA256_BYTES_IN_WORD, ctx->H[7]); + } /* Erase potentially sensitive data. */ memset (ctx, 0, sizeof(struct sha256_ctx)); diff --git a/src/microhttpd/sha256.h b/src/microhttpd/sha256.h index 88a558fc..3b866af2 100644 --- a/src/microhttpd/sha256.h +++ b/src/microhttpd/sha256.h @@ -30,25 +30,42 @@ #include #include + /** * Digest is kept internally as 8 32-bit words. */ #define _SHA256_DIGEST_LENGTH 8 +/** + * Number of bits in single SHA-256 word + */ +#define SHA256_WORD_SIZE_BITS 32 + +/** + * Number of bytes in single SHA-256 word + * used to process data + */ +#define SHA256_BYTES_IN_WORD (SHA256_WORD_SIZE_BITS / 8) + /** * Size of SHA-256 digest in bytes */ -#define SHA256_DIGEST_SIZE (_SHA256_DIGEST_LENGTH * 4) +#define SHA256_DIGEST_SIZE (_SHA256_DIGEST_LENGTH * SHA256_BYTES_IN_WORD) /** - * Size of SHA-256 digest string in chars + * Size of SHA-256 digest string in chars including termination NUL */ #define SHA256_DIGEST_STRING_SIZE ((SHA256_DIGEST_SIZE) * 2 + 1) +/** + * Size of single processing block in bits + */ +#define SHA256_BLOCK_SIZE_BITS 512 + /** * Size of single processing block in bytes */ -#define SHA256_BLOCK_SIZE 64 +#define SHA256_BLOCK_SIZE (SHA256_BLOCK_SIZE_BITS / 8) struct sha256_ctx -- cgit v1.2.3