md5 예제 c 소스

본 포스트는 데이터 무결성 검사에 많이 사용되는 md5 예제 c 소스를 싣고 있다.

md5 128bits 암호화 hash함수로, rfc1321(https://www.ietf.org/rfc/rfc1321)로 지정되어 있다. 암호화 함수이지만, md5의 암호화에 결함이 발견되어 사용되지 않는다고 한다. (위키백과 https://ko.wikipedia.org/wiki/MD5 참조)

아래의 md5 c 소스 코드는 위키백과의 md5 문서와 rfc1321을 참조해 구현하였다.
rfc1321의 소스 코드와는 다른 코드이다. 



  • API

int md5_reset(md5_t *ctx);
- md5 데이터 구조체를 초기화하는 함수, 최초 이 함수를 호출해야 한다.

int md5_update(md5_t *ctx, char *msg, int msg_len);
- msg 데이터를 512bits단위로 잘라 hash를 업데이트 한다. 큰 데이터를 여러 번 나눠 호출해도 되도록 처리했다. 

int md5_getdigest(md5_t *ctx, unsigned char digest[16]);
- 처리 안된 나머지 데이터를 512bits가 되도록 아래 형태를 만들어 md5 hash 코드 업데이트한 후, hash 코드를 digest 변수에 넣어 반환한다. 
  "버퍼에 남은 데이터 + 0x80 + padding + 데이터 bits 사이즈(64bits 크기로) "


  • md5 c 소스 코드

/*=======================================================
INCLUDE
=======================================================*/

#include <stdio.h>
#include <malloc.h>
#include <string.h>

/*=======================================================
DEFINE
=======================================================*/

#define leftrotate(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
#define md5_block_bytes    64 // 512bits
#define md5_buffer_size     (md5_block_bytes*2)

typedef struct _md5_t
{
unsigned int h0;
unsigned int h1;
unsigned int h2;
unsigned int h3;

char buffer[md5_buffer_size];
unsigned int buffer_stored_len;

unsigned __int64 updated_len;

}md5_t;

/*=======================================================
LOCAL VARIABLE
=======================================================*/

static const unsigned int md5_r[md5_block_bytes] =
{
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 unsigned int md5_k[md5_block_bytes] =
{
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
};

/*=======================================================
LOCAL FUNCTION
=======================================================*/
void md5_puthash(unsigned int hash, unsigned char *dst)
{
if (!dst) return;

dst[0] = (unsigned char)(hash);
dst[1] = (unsigned char)(hash >> 8);
dst[2] = (unsigned char)(hash >> 16);
dst[3] = (unsigned char)(hash >> 24);
}

int md5_block_update(md5_t *ctx,char *block)
{
int i = 0;
unsigned int a,b,c,d;
unsigned int *w;

if (!ctx || !block)
return -1;

a = ctx->h0;
b = ctx->h1;
c = ctx->h2;
d = ctx->h3;
w = (unsigned int*)block;

while (i < md5_block_bytes)
{
unsigned int f = 0;
unsigned int g = 0;
unsigned int temp = 0;

if (i < 16)
{
f = (b & c) | ((~b) & d);
g = i;
}
else if (i < 32)
{
f = (d & b) | ((~d) & c);
g = (5 * i + 1) % 16;
}
else if (i < 48)
{
f = b ^ c ^ d;
g = (3 * i + 5) % 16;
}
else
{
f = c ^ (b | (~d));
g = (7 * i) % 16;
}

temp = d;
d = c;
c = b;
b = b + leftrotate((a + f + md5_k[i] + w[g]), md5_r[i]);
a = temp;

i++;
}

ctx->h0 += a;
ctx->h1 += b;
ctx->h2 += c;
ctx->h3 += d;
ctx->updated_len += md5_block_bytes;
return 0;
}

/*=======================================================
GLOBAL FUNCTION
=======================================================*/
int md5_reset(md5_t *ctx)
{
if (!ctx) return -1;

memset(ctx, 0, sizeof(md5_t));
ctx->h0 = 0x67452301;
ctx->h1 = 0xefcdab89;
ctx->h2 = 0x98badcfe;
ctx->h3 = 0x10325476;

ctx->buffer_stored_len = 0;
ctx->updated_len = 0;

return 0;
}

int md5_update(md5_t *ctx, char *msg, int msg_len)
{
int remain_len = msg_len;
int offset = 0;

if (!ctx || !msg || msg_len <= 0) return -1;
if (ctx->buffer_stored_len < 0) return -1;

if (ctx->buffer_stored_len > 0)
{
int to_copy_len = md5_block_bytes - ctx->buffer_stored_len;
to_copy_len = (to_copy_len > remain_len) ? remain_len : to_copy_len;

memcpy(&ctx->buffer[ctx->buffer_stored_len], msg, to_copy_len);

ctx->buffer_stored_len += to_copy_len;
remain_len -= to_copy_len;
offset += to_copy_len;

if (ctx->buffer_stored_len == md5_block_bytes)
{
md5_block_update(ctx, ctx->buffer);
ctx->buffer_stored_len = 0;
}
}

while (remain_len >= md5_block_bytes)
{
md5_block_update(ctx, &msg[offset]);

remain_len -= md5_block_bytes;
offset += md5_block_bytes;
}

if (remain_len)
{
memcpy(ctx->buffer, &msg[offset], remain_len);
ctx->buffer_stored_len = remain_len;
}
return 0;
}

int md5_getdigest(md5_t *ctx, unsigned char digest[16])
{
int offset = 0;
unsigned __int64 total_msg_bits_size = 0;

if (!ctx) return -1;

offset = ctx->buffer_stored_len;
total_msg_bits_size = 8 * (ctx->updated_len + (unsigned __int64)ctx->buffer_stored_len);

ctx->buffer[offset++] = 128;
memset(&ctx->buffer[offset], 0, md5_buffer_size - offset);

if (offset <= md5_block_bytes - sizeof(__int64))
{
offset = md5_block_bytes - sizeof(__int64);
}
else
{
offset = md5_buffer_size - sizeof(__int64);
}

// little-endian
memcpy(&ctx->buffer[offset], &total_msg_bits_size, sizeof(__int64));
offset += sizeof(__int64);

md5_block_update(ctx, ctx->buffer);
if (offset > md5_block_bytes) md5_block_update(ctx, &ctx->buffer[md5_block_bytes]);


/*
digest에 hash코드를 넣는다.
*/
md5_puthash(ctx->h0, digest);
md5_puthash(ctx->h1, digest + 4);
md5_puthash(ctx->h2, digest + 8);
md5_puthash(ctx->h3, digest + 12);

return 0;
}


  • 사용 예제 코드

void md5(char *msg)
{
int i = 0;
unsigned char digest[16] = { 0, };
md5_t md5_ctx;

md5_reset(&md5_ctx);
md5_update(&md5_ctx, msg, strlen(msg));
md5_getdigest(&md5_ctx, digest);

printf("MD5 (\"%s\") = ", msg);
for (i = 0; i < 16; i++)printf("%02x", digest[i]);
printf("\n");

}

위 함수를 사용하여 md5 코드가 잘 만들어졌는지 rfc1321에 있는 테스트 결과와 비교해보았다.

md5("");
md5("a");
md5("abc");
md5("message digest");
md5("abcdefghijklmnopqrstuvwxyz");
md5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
md5("12345678901234567890123456789012345678901234567890123456789012345678901234567890");

실행 결과는 아래와 같다.


rfc1321의 테스트 결과와 비교하니 md5 코드가 잘 만들어진 것 같다. 

댓글

이 블로그의 인기 게시물

간단한 cfar 알고리즘에 대해

windows에서 간단하게 크롬캐스트(Chromecast)를 통해 윈도우 화면 미러링 방법

간단한 칼만 필터(Kalman Filter) 소스 코드와 사용 예제

안드로이드(android) 전체 화면 시계 앱(clock app) 예제 코드

mkfs.fat Device or resource busy 에러 해결법