Message Digest, FingerPrint - WindyHana's Solanara
목차
개요
메시지 다이제스트(Message Digest)란, 메시지를 해시(Hash)하는 것을 의미한다. 임의의 길이를 가진 메시지를 MD함수에 넣으면 '일정한 길이'의 출력을 가진 데이터를 얻는다. 이 데이터를 비교해 위/변조 되었는지 쉽게 알 수 있다.
MD5(Message-Digest algorithm 5)는 128비트 메시지 해시 알고리즘이다. RFC1321로 등록되어있다. 1991년 MD4를 대신해 개발되었다. 하지만 1996년 설계상의 문제가 발견되고1) 2006년 결국 충돌을 찾아내는 알고리즘까지 개발되었다. 따라서 심각한 보안 위협을 가져올 수 있기 때문에, 인증서 비교 용도에는 사용하지 않을 것을 권장한다. (하지만 아직까지도 파일 무결성 검사에는 유용하다)
SHA-1(Secure Hash Algorithm 1)은 1995년 NSA(미 국가 안전 보장국)이 개발한 160비트 메시지 해시 알고리즘이다. 1993년 개발된 SHA-0의 보안 문제를 보안하기 위해 만들었다. (현재는 SHA-0를 사용하지 않는다) 기본적인 알고리즘은 MD5와 비슷하다.
이후 NSA는 SHA-1을 보완한 몇가지 변형 알고리즘을 내 놓았다. SHA-1도 현재는 안전하지 않은 것으로 알려져 있으며, 조만간 SHA-256이나 SHA-512로 대체될 예정이다.
1) How to Break MD5 and Other Hash Functions
digest 커맨드
솔라리스 10에서는 MD5라이브러리와 라이브러리를 이용한 계산 프로그램이 설치되어있다. 파일에 대한 해시를 구하려면 다음과 같이 하면 된다.
digest -a 알고리즘명 파일명
digest 에서 지원하는 모든 알고리즘을 보려면 다음과 같이 한다.
root@wl ~ # digest -l
sha1
md5
sha256
sha384
sha512
아래에 예제가 있다.
root@wl ~ # mkfile 1m sample
root@wl ~ # digest -a md5 sample
b6d81b360a5672d80c27430f39153e2c
root@wl ~ # digest -v -a md5 sample
md5 (sample) = b6d81b360a5672d80c27430f39153e2c
root@wl ~ # digest -a sha1 sample
3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
root@wl ~ # digest -a sha256 sample
30e14955ebf1352266dc2ff8067e68104607e750abb9d3b36582b8af909fcb58
root@wl ~ # digest -a sha384 sample
3164673a8ac27576ab5fc06b9adc4ce0aca5bd3025384b1cf2128a8795e747c431e882785a0bf8dc70b42995db388575
root@wl ~ # digest -a sha512 sample
d6292685b380e338e025b3415a90fe8f9d39a46e7bdba8cb78c50a338cefca741f69e4e46411c32de1afdedfb268e579a51f81ff85e56f55b0ee7c33fe8c25c9
OpenSSL의 해시함수
libmd를 사용한 구현
MD라이브러리(솔라리스 10 이상)를 이용해 MD5와 SHA1, SHA2 계산 프로그램을 구현했다. 솔라리스 기본 라이브러리에서는 SHA-224를 지원하지 않는다.
/*
Hash(MD5, SHA-1, SHA-2) Sample - by using solaris MD library
WindyHana's Solanara http://www.solanara.net/
cc -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -lmd -m64 -fast -o digest_solaris digest_solaris.c
*/
#include <md5.h>
#include <sha1.h>
#include <sha2.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define BUF_SIZE (64 * 1024)
void printusage(char* argv[]) {
printf("\n");
printf("Digest Utility for Solaris v3\n");
printf(" - with Solaris MD Library.\n");
#if _FILE_OFFSET_BITS - 0 == 64
printf(" - with support 64 bit file offset.\n");
#endif
printf("Copyright (C) Jo HoSeok 2005-2011. All rights reserved.\n");
printf("\n");
printf("USEAGE:\n");
printf(" %s [-a algorithm] [-h] FILENAME\n", argv[0]);
printf("\n");
printf(" -a algorithm: md5, sha1, sha256, sha384, sha512. default: sha256\n");
printf(" -h: Help(this screen)\n");
printf("\n");
printf("OUTPUT:\n");
printf(" DIGEST_MESSAGE ALGORITHM FILESIZE\n");
printf("EXAMPLE:\n");
printf(" %s -a sha256 /bin/ls\n", argv[0]);
printf("\n");
}
int main(int argc, char* argv[])
{
int alg = 3; // algorithm number. 1=MD5, 2=SHA1, 3=SHA256, 4=SHA384, 5=SHA512
char * algname = "sha256"; // algorithm name.
FILE * fp = NULL;
int c, errflg = 0; // getopt 에서 사용하는 임시 변수
while ((c = getopt(argc, argv, ":ha:")) != -1) {
switch(c) {
case 'a':
if (strcmp("md5", optarg) == 0) {
alg = 1;
algname = "md5";
} else if (strcmp("sha1", optarg) == 0) {
alg = 2;
algname = "sha1";
} else if (strcmp("sha256", optarg) == 0) {
alg = 3;
algname = "sha256";
} else if (strcmp("sha384", optarg) == 0) {
alg = 4;
algname = "sha384";
} else if (strcmp("sha512", optarg) == 0) {
alg = 5;
algname = "sha512";
} else {
fprintf(stderr, "illegal argument: -%c %s\n", optopt, optarg);
errflg++;
}
break;
case 'h':
printusage(argv);
return 0;
case ':':
if (optopt == '-') {
fprintf(stderr, "%s option requires an argument.\n", argv[optind-1]);
} else {
fprintf(stderr, "-%c option requires an argument.\n", optopt);
}
errflg++;
break;
case '?':
if (isprint(optopt)) {
fprintf(stderr, "illegal option: -%c\n", optopt);
} else {
fprintf(stderr, "illegal option: \\x%x.\n", optopt);
}
errflg++;
break;
}
}
if (errflg > 0) {
printusage(argv);
return 1;
}
if (optind < argc) {
fp = fopen(argv[optind], "rb");
if (!fp) {
fprintf(stderr, "Cannot Open %s.\r\n", argv[optind]);
return 1;
}
} else {
printusage(argv);
return 0;
}
unsigned char* auth_buffer; // 버퍼
char* hex_output; // 결과값을 헥스 값으로 변환한 값. + 1 은 null 값
long long offset = 0;
off_t readcount; // 읽은 바이트수, 파일 오프셋 위치
int result_msgcnt = 0;
auth_buffer = (void *) malloc(BUF_SIZE); // 버퍼
if (alg == 1) {
MD5_CTX md5_context; // MD5 컨텍스트 값
MD5Init(&md5_context);
while (readcount = fread(auth_buffer, 1, BUF_SIZE, fp)) {
offset += readcount;
MD5Update(&md5_context, auth_buffer, readcount);
}
MD5Final(auth_buffer, &md5_context);
hex_output = malloc(16*2 + 1);
result_msgcnt = 16;
} else if (alg == 2) {
SHA1_CTX sha1_context; // SHA1 컨텍스트 값
SHA1Init(&sha1_context);
while (readcount = fread(auth_buffer, 1, BUF_SIZE, fp)) {
offset += readcount;
SHA1Update(&sha1_context, auth_buffer, readcount);
}
SHA1Final(auth_buffer, &sha1_context);
hex_output = malloc(20*2 + 1);
result_msgcnt = 20;
} else if (alg == 3 || alg == 4 || alg == 5) {
SHA2_CTX sha2_context; // SHA2 컨텍스트 값
if (alg == 3) {
SHA2Init(SHA256, &sha2_context);
hex_output = malloc(32*2 + 1);
result_msgcnt = 32;
} else if (alg == 4) {
SHA2Init(SHA384, &sha2_context);
hex_output = malloc(48*2 + 1);
result_msgcnt = 48;
} else if (alg == 5) {
SHA2Init(SHA512, &sha2_context);
hex_output = malloc(64*2 + 1);
result_msgcnt = 64;
}
while (readcount = fread(auth_buffer, 1, BUF_SIZE, fp)) {
offset += readcount;
SHA2Update(&sha2_context, auth_buffer, readcount);
}
SHA2Final(auth_buffer, &sha2_context);
}
fclose(fp);
// 16진수로 표현
int i;
for (i = 0; i < result_msgcnt; i++) {
sprintf(hex_output + i * 2, "%02x", auth_buffer[i]);
}
printf("%s %s %lld\n", hex_output, algname, offset);
free(auth_buffer);
free(hex_output);
return 0;
}
root@wl ~ # cc -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -lmd -m64 -fast -o digest_solaris digest_solaris.c
FingerPrint
※ 말 그대로 [지문]이다. 솔라리스에 설치된 파일에 대해, 이 파일이 변조되었는지 MD5해시값을 통해 확인할 수 있다. 어떤 파일인지 어떤 패키지인지, 어떤 패치를 설치했는지 나온다.
root@wl ~ $ digest -a md5 /bin/sh
4918cc526b37468c04c702100b1da7dd
root@wl ~ $
Solaris Fingerprint Database An Identification Tool for Solaris Software and Files 에서 MD5해시값을 입력하면 아래와 같이 나온다.
4918cc526b37468c04c702100b1da7dd - - 1 match(es)
canonical-path: /sbin/sh
package: SUNWcsr
version: 11.10.0,REV=2005.01.21.16.34
architecture: i386
source: Solaris 10/x86
patch: 138254-02
컴파일러 옵션에 따른 성능
최적화된 옵션
컴파일시 -fast 옵션을 붙이느냐 안 붙이느냐에 따라 성능에 차이가 있다. 보통 CPU의 캐시 메모리 및 인스트럭션 셋에 맞춰 컴파일 해준다. 또한 -m64 를 붙인 경우 64bit로 컴파일한다.
root@wl ~ # cc -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -lmd5 -lmd -m64 -fast -o digest_solaris digest_solaris.c
root@wl ~ # time ./digest_solaris -a sha1 4GFile
1bf99ee9f374e58e201e4dda4f474e570eb77229 4294967296 sha1
real 0m47.042s
user 0m17.512s
sys 0m2.486s
솔라리스 10 기본 소프트웨어
참고삼아 솔라리스 10에 들어있는 기본 툴에 대해 재어보았다.
root@wl ~ # time digest -a sha1 4GFile
1bf99ee9f374e58e201e4dda4f474e570eb77229
real 1m0.908s
user 0m25.883s
sys 0m13.137s