| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * mpq-md5.c -- MD5 checksums for MPQ format compatibility. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003-2026 Maik Broemme <mbroemme@libmpq.org> | ||
| 5 | * | ||
| 6 | * This file is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU Lesser General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2.1 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This file is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public License | ||
| 17 | * along with this file; if not, see <https://www.gnu.org/licenses/>. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include "mpq-md5.h" | ||
| 21 | #include "mpq-endian.h" | ||
| 22 | |||
| 23 | #include <string.h> | ||
| 24 | |||
| 25 | /* | ||
| 26 | * Transform one complete block using fixed-width unsigned arithmetic. | ||
| 27 | * Explicit little-endian loads make the result independent of host byte order. | ||
| 28 | */ | ||
| 29 | static void | ||
| 30 | 132613 | transform(mpq_md5_s *context, const uint8_t block[64]) | |
| 31 | { | ||
| 32 | static const uint32_t constants[64] = { | ||
| 33 | 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, | ||
| 34 | 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, | ||
| 35 | 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, | ||
| 36 | 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, | ||
| 37 | 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, | ||
| 38 | 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, | ||
| 39 | 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, | ||
| 40 | 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, | ||
| 41 | 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, | ||
| 42 | 0xeb86d391 | ||
| 43 | }; | ||
| 44 | static const uint8_t shifts[16] = { 7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21 }; | ||
| 45 | uint32_t words[16]; | ||
| 46 | 132613 | uint32_t a = context->state[0]; | |
| 47 | 132613 | uint32_t b = context->state[1]; | |
| 48 | 132613 | uint32_t c = context->state[2]; | |
| 49 | 132613 | uint32_t d = context->state[3]; | |
| 50 | uint32_t i; | ||
| 51 | |||
| 52 |
2/2✓ Branch 0 taken 2121808 times.
✓ Branch 1 taken 132613 times.
|
2254421 | for (i = 0; i < 16; ++i) |
| 53 | 2121808 | words[i] = libmpq__load_le32(block + 4 * i); | |
| 54 |
2/2✓ Branch 0 taken 8487232 times.
✓ Branch 1 taken 132613 times.
|
8619845 | for (i = 0; i < 64; ++i) { |
| 55 | uint32_t f; | ||
| 56 | uint32_t index; | ||
| 57 | uint32_t value; | ||
| 58 | 8487232 | uint32_t previous = d; | |
| 59 | 8487232 | uint8_t shift = shifts[(i / 16) * 4 + i % 4]; | |
| 60 |
2/2✓ Branch 0 taken 2121808 times.
✓ Branch 1 taken 6365424 times.
|
8487232 | if (i < 16) { |
| 61 | 2121808 | f = (b & c) | (~b & d); | |
| 62 | 2121808 | index = i; | |
| 63 |
2/2✓ Branch 0 taken 2121808 times.
✓ Branch 1 taken 4243616 times.
|
6365424 | } else if (i < 32) { |
| 64 | 2121808 | f = (d & b) | (~d & c); | |
| 65 | 2121808 | index = (5 * i + 1) % 16; | |
| 66 |
2/2✓ Branch 0 taken 2121808 times.
✓ Branch 1 taken 2121808 times.
|
4243616 | } else if (i < 48) { |
| 67 | 2121808 | f = b ^ c ^ d; | |
| 68 | 2121808 | index = (3 * i + 5) % 16; | |
| 69 | } else { | ||
| 70 | 2121808 | f = c ^ (b | ~d); | |
| 71 | 2121808 | index = (7 * i) % 16; | |
| 72 | } | ||
| 73 | 8487232 | value = a + f + constants[i] + words[index]; | |
| 74 | 8487232 | d = c; | |
| 75 | 8487232 | c = b; | |
| 76 | 8487232 | b += (value << shift) | (value >> (32 - shift)); | |
| 77 | 8487232 | a = previous; | |
| 78 | } | ||
| 79 | 132613 | context->state[0] += a; | |
| 80 | 132613 | context->state[1] += b; | |
| 81 | 132613 | context->state[2] += c; | |
| 82 | 132613 | context->state[3] += d; | |
| 83 | 132613 | } | |
| 84 | |||
| 85 | /* | ||
| 86 | * Initialize a caller-owned incremental checksum context. | ||
| 87 | * No allocation or external cryptographic provider is needed. | ||
| 88 | */ | ||
| 89 | void | ||
| 90 | 2980 | libmpq__md5_init(mpq_md5_s *context) | |
| 91 | { | ||
| 92 | 2980 | memset(context, 0, sizeof(*context)); | |
| 93 | 2980 | context->state[0] = 0x67452301; | |
| 94 | 2980 | context->state[1] = 0xefcdab89; | |
| 95 | 2980 | context->state[2] = 0x98badcfe; | |
| 96 | 2980 | context->state[3] = 0x10325476; | |
| 97 | 2980 | } | |
| 98 | |||
| 99 | /* | ||
| 100 | * Add bytes to the checksum, buffering at most one incomplete block. | ||
| 101 | * The byte count wraps modulo 2^64 as required for the encoded bit length. | ||
| 102 | */ | ||
| 103 | void | ||
| 104 | 15617 | libmpq__md5_update(mpq_md5_s *context, const uint8_t *data, size_t size) | |
| 105 | { | ||
| 106 |
2/2✓ Branch 0 taken 139352 times.
✓ Branch 1 taken 15617 times.
|
154969 | while (size != 0) { |
| 107 | 139352 | size_t used = (size_t)(context->size % 64); | |
| 108 | 139352 | size_t take = size < 64 - used ? size : 64 - used; | |
| 109 | 139352 | memcpy(context->buffer + used, data, take); | |
| 110 | 139352 | context->size += take; | |
| 111 | 139352 | data += take; | |
| 112 | 139352 | size -= take; | |
| 113 |
2/2✓ Branch 0 taken 132613 times.
✓ Branch 1 taken 6739 times.
|
139352 | if (context->size % 64 == 0) |
| 114 | 132613 | transform(context, context->buffer); | |
| 115 | } | ||
| 116 | 15617 | } | |
| 117 | |||
| 118 | /* | ||
| 119 | * Append RFC padding and return the canonical sixteen-byte digest. | ||
| 120 | * Finalization consumes the context and clears its buffered input. | ||
| 121 | */ | ||
| 122 | void | ||
| 123 | 2963 | libmpq__md5_final(mpq_md5_s *context, uint8_t digest[LIBMPQ_MD5_SIZE]) | |
| 124 | { | ||
| 125 | 2963 | uint8_t padding[64] = { 0x80 }; | |
| 126 | uint8_t length[8]; | ||
| 127 | 2963 | size_t used = (size_t)(context->size % 64); | |
| 128 | uint32_t i; | ||
| 129 | |||
| 130 | 2963 | libmpq__store_le64(length, context->size * 8); | |
| 131 |
2/2✓ Branch 0 taken 2850 times.
✓ Branch 1 taken 113 times.
|
2963 | libmpq__md5_update(context, padding, used < 56 ? 56 - used : 120 - used); |
| 132 | 2963 | libmpq__md5_update(context, length, sizeof(length)); | |
| 133 |
2/2✓ Branch 0 taken 11852 times.
✓ Branch 1 taken 2963 times.
|
14815 | for (i = 0; i < 4; ++i) |
| 134 | 11852 | libmpq__store_le32(digest + i * 4, context->state[i]); | |
| 135 | 2963 | memset(context, 0, sizeof(*context)); | |
| 136 | 2963 | } | |
| 137 |