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)를 통해 윈도우 화면 미러링 방법

쉽게 설명한 파티클 필터(particle filter) 동작 원리와 예제

base64 인코딩 디코딩 예제 c 소스

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