#include #include // memcpy #include // uint8_t, uint32_t #include // размер чанка #define CHUNK_SIZE 64 // функции раундов #define funcF(x, y, z) ((x & y) | ((~x) & z)) #define funcG(x, y, z) ((x & z) | ((~z) & y)) #define funcH(x, y, z) (x ^ y ^ z) #define funcI(x, y, z) (y ^ ((~z) | x)) // битовый сдвиг #define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) void md5(uint8_t *data, size_t size, uint32_t *result) { // размеры сдвигов uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; // таблица констант uint32_t k[] = { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}; // инициализация переменных под результат result[0] = 0x67452301; result[1] = 0xefcdab89; result[2] = 0x98badcfe; result[3] = 0x10325476; size_t bits_len = size * 8; // длина хешируемых данных в битах int chunk_i; // счетчик цикла по чанкам uint8_t chunk[CHUNK_SIZE]; // чанк (64 байта = 512 бит) uint8_t sub_chunk[2][CHUNK_SIZE]; // доп. чанк для остатка данных (56 байт = 448 бит + 2 байта bits_len) int sub_chunk_flag = 0; // флаг наличия доп. чанка, а так же помогает взять нужный чанк на копирование int chunk_count = size / CHUNK_SIZE; // количество чанков uint32_t *chunk_4b; // 32 битное представление элемента чанка для мат операций (64 байта в 16 элементах) uint32_t a, b, c, d; // переменные для расчетов в раундах uint32_t i; // счетчик цикла для раундов uint32_t temp; // буфер для перестановки местами значений двух переменных uint32_t f, g; // результат функции раунда и индекс в массиве чанка // цикл по чанкам for (chunk_i = 0; chunk_i <= chunk_count; chunk_i++) { // обнуляем чанк memset(chunk, 0, CHUNK_SIZE); // если индекс меньше кол-ва чанков - используется полный размер чанка if (chunk_i < chunk_count) { // копируем данные в чанк memcpy(chunk, data + (chunk_i * CHUNK_SIZE), CHUNK_SIZE); } // if (chunk_i < chunk_count) then если индекс меньше кол-ва чанков // иначе требуется сформировать по правилам (добавить бит и в конце дописать размер данных в битах) else { // если флаг не установлен, производится подготовка доп чанка if (sub_chunk_flag == 0) { // установка флага для единичной инициализации sub_chunk_flag = 1; // обнулим чанки memset(sub_chunk, 0, CHUNK_SIZE * 2); // скопируем данные в первый чанк memcpy(sub_chunk, data + (chunk_i * CHUNK_SIZE), (size % CHUNK_SIZE)); // допишем в конец данных единичный бит sub_chunk[0][size % CHUNK_SIZE] = 128; // если остаток от деления размера данных на размер чанка позволяет записать байт с единичным битом и размер данных (в битах) 8 байт (16 бит) if ((size % CHUNK_SIZE) <= 55) { // то используется только один доп чанк, скопируем размер данных (в битах) memcpy(&sub_chunk[0][56], &bits_len, sizeof(bits_len)); } // if ((size % CHUNK_SIZE) <= 55) then else { // иначе используется второй доп чанк memcpy(&sub_chunk[1][56], &bits_len, sizeof(bits_len)); // отнимем единицу у счетчика - еще один заход на доп. чанк chunk_i--; } // if ((size % CHUNK_SIZE) <= 55) else } // if (sub_chunk_flag == 0) если флаг не установлен, производится подготовка доп чанка // копируется доп чанк в соответсвии с значением флага-1 memcpy(chunk, sub_chunk[sub_chunk_flag - 1], CHUNK_SIZE); sub_chunk_flag++; // инкремент на случай следующей итерации со вторым доп чанком } // if (chunk_i < chunk_count) else если индекс больше или равен кол-ву чанков // 4 байтовый указатель на чанк (16 эл-тов) chunk_4b = (uint32_t *)chunk; // инициализируем переменные для расчетов a = result[0]; b = result[1]; c = result[2]; d = result[3]; // цикл основных преобразований for (i = 0; i < 64; i++) { // определяем раунд switch (i / 16) { case 0: f = funcF(b, c, d); g = i; break; case 1: f = funcG(b, c, d); g = (5 * i + 1) % 16; break; case 2: f = funcH(b, c, d); g = (3 * i + 5) % 16; break; case 3: f = funcI(b, c, d); g = (7 * i) % 16; break; } // switch(i/16) определяем раунд // преобразования temp = d; d = c; c = b; b += LEFTROTATE((a + f + k[i] + chunk_4b[g]), r[i]); a = temp; } // for(i) цикл основных преобразований // после прохода чанка - модифицируем переменную результата result[0] += a; result[1] += b; result[2] += c; result[3] += d; } // for(chunk_i) цикл по чанкам } // void md5(uint8_t *data, size_t size, uint32_t *result) int main(int argc, char *argv[]) { char msg[] = "md5"; size_t len = sizeof(msg) - 1; // минус нуль-терминатор // результат uint32_t result[4]; // вызов хеш-функции md5((uint8_t *)msg, len, result); //вывод результата в формате little-endian uint8_t *p; p = (uint8_t *)&result[0]; printf("%2.2x%2.2x%2.2x%2.2x ", p[0], p[1], p[2], p[3]); p = (uint8_t *)&result[1]; printf("%2.2x%2.2x%2.2x%2.2x ", p[0], p[1], p[2], p[3]); p = (uint8_t *)&result[2]; printf("%2.2x%2.2x%2.2x%2.2x ", p[0], p[1], p[2], p[3]); p = (uint8_t *)&result[3]; printf("%2.2x%2.2x%2.2x%2.2x\n", p[0], p[1], p[2], p[3]); //"1111111111111111111111111111111111111111111111111111111111111111" = 46f04863 257ac804 0905ea00 02183d35 //"1" = c4ca4238 a0b92382 0dcc509a 6f75849b //"md5" = 1bc29b36 f623ba82 aaf6724f d3b16718 return 0; }