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 코드가 잘 만들어진 것 같다.
댓글
댓글 쓰기