This commit is contained in:
Lisoveliy 2025-06-21 20:25:01 +03:00
commit 843cbdcdf1
6 changed files with 208 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.vscode/settings.json
obj*
bin*

23
Makefile Normal file
View File

@ -0,0 +1,23 @@
CC = clang
CFLAGS = -Wall -Wextra -pedantic -std=c17 -g
SRCS = src/main.c src/md5.c
OBJS = $(SRCS:src/%.c=obj/%.o)
TARGET = bin/md5hash
all: $(TARGET)
$(TARGET): $(OBJS)
mkdir -p bin
$(CC) $(OBJS) -o $@
obj/%.o: src/%.c | obj
$(CC) $(CFLAGS) -c $< -o $@
obj:
mkdir -p obj
clean:
rm -rf ./obj $(TARGET)
.PHONY: clean

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# MD5 Hash Calculator
Simple MD5 realization for small amounts of data written on pure C for Linux

56
src/main.c Normal file
View File

@ -0,0 +1,56 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "md5.h"
void returnHash(uint8_t* digest);
int main(int argc, char** argv){
if(argc > 1)
{
char* argData = argv[1];
FILE* sFile = fopen(argData, "rb");
uint8_t output[16]; //128-bit buffer
if(sFile == NULL){
md5((uint8_t*) argData, strlen(argData), output);
}
else{
//Prepare data for stream into md5
fseek(sFile, 0, SEEK_END);
unsigned long fileSize = ftell(sFile);
rewind(sFile);
uint8_t *stream = (uint8_t*)malloc(fileSize);
if (!stream) {
fclose(sFile);
errno = -1;
perror("malloc() failed. Maybe file is too big?");
return errno;
}
size_t readSize = fread(stream, 1, fileSize, sFile);
fclose(sFile);
if (readSize != fileSize) {
free(stream);
errno = -2;
perror("Failed to read file. Maybe file is too big?");
return errno;
}
md5(stream, fileSize, output);
}
returnHash(output);
return 0;
}
errno = 1;
perror("There is no input for generate MD5 hash");
return 1;
}
void returnHash(uint8_t* digest){
for (size_t i = 0; i < 16; i++) {
printf("%02x", digest[i]);
}
}

118
src/md5.c Normal file
View File

@ -0,0 +1,118 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
// Левое циклическое вращение (ROTATE LEFT)
#define LEFT_ROTATE(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
// Константы для сдвигов
static const uint8_t s[] = {
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
};
// Таблица предварительно вычисленных констант
static const 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
};
// Функции для раундов
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
void md5(const uint8_t *initial_msg, size_t initial_len, uint8_t *digest) {
// Инициализация буфера
uint32_t a0 = 0x67452301;
uint32_t b0 = 0xefcdab89;
uint32_t c0 = 0x98badcfe;
uint32_t d0 = 0x10325476;
// Вычисление необходимой длины сообщения с дополнением
size_t new_len = (((initial_len + 8) / 64) + 1) * 64;
uint8_t *msg = (uint8_t*)malloc(new_len);
memcpy(msg, initial_msg, initial_len);
msg[initial_len] = 0x80; // Добавление бита "1"
// Заполнение нулями
for (size_t i = initial_len + 1; i < new_len - 8; i++) {
msg[i] = 0;
}
// Добавление длины сообщения в битах (little-endian)
uint64_t bit_len = (uint64_t)initial_len * 8;
memcpy(msg + new_len - 8, &bit_len, 8);
// Обработка сообщения блоками по 64 байта
for (size_t offset = 0; offset < new_len; offset += 64) {
// Разбиение текущего блока на 16 слов
uint32_t M[16];
for (size_t i = 0; i < 16; i++) {
M[i] = *(uint32_t*)(msg + offset + i * 4);
}
// Инициализация рабочих переменных
uint32_t A = a0;
uint32_t B = b0;
uint32_t C = c0;
uint32_t D = d0;
// Основной цикл (64 шага)
for (size_t i = 0; i < 64; i++) {
uint32_t F, g;
if (i < 16) {
F = F(B, C, D);
g = i;
} else if (i < 32) {
F = G(B, C, D);
g = (5 * i + 1) % 16;
} else if (i < 48) {
F = H(B, C, D);
g = (3 * i + 5) % 16;
} else {
F = I(B, C, D);
g = (7 * i) % 16;
}
// Вычисление нового значения
F = F + A + K[i] + M[g];
A = D;
D = C;
C = B;
B = B + LEFT_ROTATE(F, s[i]);
}
// Обновление состояния
a0 += A;
b0 += B;
c0 += C;
d0 += D;
}
free(msg);
// Формирование итогового дайджеста в порядке little-endian
memcpy(digest, &a0, 4);
memcpy(digest + 4, &b0, 4);
memcpy(digest + 8, &c0, 4);
memcpy(digest + 12, &d0, 4);
}

5
src/md5.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <stdio.h>
#include <stdint.h>
void md5(const uint8_t *initial_msg, size_t initial_len, uint8_t *digest);