Message Digest, FingerPrint - 윈디하나의 솔라나라
목차
개요
메시지 다이제스트(Message Digest)란, 메시지를 해시(Hash)하는 것을 의미한다. 임의의 길이를 가진 메시지를 MD함수에 넣으면 일정한 길이 를 가진 데이터를 얻는다. 이 데이터를 비교해 위/변조 되었는지 쉽게 알 수 있다.
MD5(Message-Digest algorithm 5)는 128비트 메시지 해시 알고리즘이다. RFC 1321 - The MD5 Message-Digest Algorithm 로 등록되어있다. 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로 완전히 대체될 것이다.
SHA-2(Secure Hash Algorithm 2)은 2001년 SHA-1을 대체하기 위해 만든 해시 알고리즘이다. SHA-256, SHA-384, SHA-512 이 정의되어있다. 현재 표준 알고리즘으로 널리 사용되고 있다.
SHA-224는 2004년 2월에 업데이트된 SHA-2의 다른 버전이다. SHA-256으로 계산하다 마지막에 출력을 224비트로 줄이는 알고리즘이다. 2키-3DES와 맞추기 위해 개발되었다. (현재 2키-3DES 는 사용하지 않는다)
SHA-512/224, SHA-512/256은 2012년 3월에 업데이트 된 SHA-512의 축소 버전이다. 알고리즘은 SHA-512 와 동일하게 계산하지만, 최종 값 도출에 SHA-224나 SHA-256과 같은 길이로 결과값을 축소한다. SHA-512이 SHA-256보다 (32비트 프로세서보다) 64비트 프로세서에서 계산이 빠르기 때문에 개발된 알고리즘이다.
SHA-3(Secure Hash Algorithm 3)은 2012년 10월 NIST의 차세대 해시 알고리즘 대회에서 우승한 Keccak 알고리즘 기반의 해시 알고리즘이다. 사실상 같은 알고리즘이지만, 초기 벡터가 다르다.
1) How to Break MD5 and Other Hash Functions
해시알고리즘 비교
Secure_Hash_Algorithm (Wikipedia) 의 내용을 바탕으로 보기 좋게 편집했다.
알고리즘 분류
알고리즘 소 분류
출력 비트수
내부 상태 비트수
블록 크기 비트수
보안 비트수 1)
MD5
128
128(4 x 32)
512
64 2)
SHA-0
160
160(5 x 32)
512
80 2)
SHA-1
160
160(5 x 32)
512
80 3)
SHA-2
SHA-224
224
256(8x32)
512
112
SHA-256
256
128
SHA-384
384
512(8x64)
1024
192
SHA-512
512
256
SHA-512/224
224
112
SHA-512/256
256
128
SHA-3
SHA3-224
224
1600(5x5x64)
1152
112
SHA3-256
256
1088
128
SHA3-384
384
832
192
SHA3-512
512
832
192
SHAKE128
d(arbitrary)
1344
min(d/2,128)
SHAKE256
d(arbitrary)
1088
min(d/2,128)
1) 출력 비트수의 절반
2) 충돌 발견
3) theoretical attack
digest 커맨드
솔라리스 10 부터는 MD5라이브러리와 라이브러리를 이용한 digest(1) 이 설치되어있다. 파일에 대한 해시를 구하려면 다음과 같이 하면 된다.
digest -a 알고리즘명 파일명
digest 에서 지원하는 모든 알고리즘을 보려면 다음과 같이 한다. 아래는 솔라리스 11.4 의 경우다.
root@wl ~ # digest -l
sha1
md5
sha224
sha256
sha384
sha512
sha512_t
sha3_224
sha3_256
sha3_384
sha3_512
아래에 예제가 있다.
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 sha224 sample
aaaadedccb998ddb99d2c020b6585a5eceadcff0c348f35fe598b418
root@wl ~ # digest -a sha256 sample
30e14955ebf1352266dc2ff8067e68104607e750abb9d3b36582b8af909fcb58
root@wl ~ # digest -a sha384 sample
3164673a8ac27576ab5fc06b9adc4ce0aca5bd3025384b1cf2128a8795e747c431e882785a0bf8dc70b42995db388575
root@wl ~ # digest -a sha512 sample
d6292685b380e338e025b3415a90fe8f9d39a46e7bdba8cb78c50a338cefca741f69e4e46411c32de1afdedfb268e579a51f81ff85e56f55b0ee7c33fe8c25c9
root@wl ~ # digest -a sha512_t -t 384 sample
3164673a8ac27576ab5fc06b9adc4ce0aca5bd3025384b1cf2128a8795e747c431e882785a0bf8dc70b42995db388575
root@wl ~ # digest -a sha512_t -t 256 sample
a4567167dee5ac6bc22a4faed8deae186603b9e10306edfff49b60ce61181a3d
root@wl ~ # digest -a sha512_t -t 224 sample
dbfd8b969178c73960e122012ff6b26d273712c36e0dc55751e57c62
root@wl ~ # digest -a sha512_t -t 160 sample
224f37d4fbd7154ffb4e47de3a5c2777f1e3bc66
root@wl ~ # digest -a sha3_224 sample
8440e0366d98ac13845eafeb06f3a01e5c38fde44ef2caef5d8048c6
root@wl ~ # digest -a sha3_256 sample
7e1839fd5b1f59802cdf1f098dd5198e49b2a242ec43a5e2f107d2e2e57b0f25
root@wl ~ # digest -a sha3_384 sample
030b0057a7765647d67b38c91f9d45ba1aa6813e01da0b128c9ef56b96b334f22fe481754cef9c2bbc3f1d5b43e78ce4
root@wl ~ # digest -a sha3_512 sample
7dab0a45cc88755f07291036b88f7a78f455c49e9832813c9e7da5f430a144fc5b6f82ad52bb9620a6aa94d2542fc0b852ab9278fce2fe5d10397ff4901ca4b7
OpenSSL의 해시함수
libmd를 사용한 구현
MD라이브러리(솔라리스 10 이상)를 이용해 MD5와 SHA1, SHA2 계산 프로그램을 구현했다. 솔라리스 기본 라이브러리에서는 SHA-224를 지원하지 않는다.
digest_solaris.c (7,739 바이트)
/*
Hash(MD5, SHA-1, SHA-2, SHA-3) Sample - by using solaris MD library
WindyHana's Solanara http://www.solanara.net/
Solaris 11.4: cc -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DSUPPORT_SHA3 -lmd -m64 -fast -o digest_solaris digest_solaris.c
or earlier : 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>
#ifdef SUPPORT_SHA3
#include <sha3.h>
#endif
#define BUF_SIZE (64 * 1024)
void printusage(char* argv[]) {
printf("\n");
printf("Digest Utility for Solaris v4\n");
printf(" - with Solaris MD Library.\n");
#if _FILE_OFFSET_BITS - 0 == 64
printf(" - with support 64 bit file offset.\n");
#endif
#ifdef SHA3_512
printf(" - with support SHA-224, SHA-512/160, SHA-512/224, SHA-512/256, SHA3.\n");
#elif SHA512_160
printf(" - with support SHA-224, SHA-512/160, SHA-512/224, SHA-512/256.\n");
#elif SHA224
printf(" - with support SHA-224.\n");
#endif
printf("Copyright (C) Jo HoSeok 2005-2018. All rights reserved.\n");
printf("\n");
printf("USEAGE:\n");
printf(" %s [-a algorithm] [-h] FILENAME\n", argv[0]);
printf("\n");
#ifdef SHA3_512
printf(" -a algorithm: md5, sha1, sha256, sha384, sha512, sha224\n");
printf(" sha512/160, sha512/224, sha512/256\n");
printf(" sha3/224, sha3/256, sha3/384, sha3/512\n");
#elif SHA512_160
printf(" -a algorithm: md5, sha1, sha256, sha384, sha512, sha224\n");
printf(" sha512/160, sha512/224, sha512/256\n");
#elif SHA224
printf(" -a algorithm: md5, sha1, sha256, sha384, sha512, sha224\n");
#else
printf(" -a algorithm: md5, sha1, sha256, sha384, sha512\n");
#endif
printf(" 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";
#ifdef SHA224
} else if (strcmp("sha224", optarg) == 0) {
alg = 6;
algname = "sha224";
#endif
#ifdef SHA512_160
} else if (strcmp("sha512/160", optarg) == 0) {
alg = 7;
algname = "sha512/160";
#endif
#ifdef SHA512_224
} else if (strcmp("sha512/224", optarg) == 0) {
alg = 8;
algname = "sha512/224";
#endif
#ifdef SHA512_256
} else if (strcmp("sha512/256", optarg) == 0) {
alg = 9;
algname = "sha512/256";
#endif
#ifdef SHA3_224
} else if (strcmp("sha3/224", optarg) == 0) {
alg = 10;
algname = "sha3/224";
#endif
#ifdef SHA3_256
} else if (strcmp("sha3/256", optarg) == 0) {
alg = 11;
algname = "sha3/256";
#endif
#ifdef SHA3_384
} else if (strcmp("sha3/384", optarg) == 0) {
alg = 12;
algname = "sha3/384";
#endif
#ifdef SHA3_512
} else if (strcmp("sha3/512", optarg) == 0) {
alg = 13;
algname = "sha3/512";
#endif
} 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;
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;
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 || alg == 6 || alg == 7 || alg == 8 || alg == 9) {
SHA2_CTX sha2_context;
if (alg == 3) {
SHA2Init(SHA256, &sha2_context);
result_msgcnt = SHA256_DIGEST_LENGTH;
} else if (alg == 4) {
SHA2Init(SHA384, &sha2_context);
result_msgcnt = SHA384_DIGEST_LENGTH;
} else if (alg == 5) {
SHA2Init(SHA512, &sha2_context);
result_msgcnt = SHA512_DIGEST_LENGTH;
#ifdef SHA224
} else if (alg == 6) {
SHA2Init(SHA224, &sha2_context);
result_msgcnt = SHA224_DIGEST_LENGTH;
#endif
#ifdef SHA512_160
} else if (alg == 7) {
SHA2Init(SHA512_160, &sha2_context);
result_msgcnt = SHA160_DIGEST_LENGTH;
#endif
#ifdef SHA512_224
} else if (alg == 8) {
SHA2Init(SHA512_224, &sha2_context);
result_msgcnt = SHA224_DIGEST_LENGTH;
#endif
#ifdef SHA512_256
} else if (alg == 9) {
SHA2Init(SHA512_256, &sha2_context);
result_msgcnt = SHA256_DIGEST_LENGTH;
#endif
}
hex_output = malloc(result_msgcnt * 2 + 1);
while (readcount = fread(auth_buffer, 1, BUF_SIZE, fp)) {
offset += readcount;
SHA2Update(&sha2_context, auth_buffer, readcount);
}
SHA2Final(auth_buffer, &sha2_context);
} else if (alg == 10 || alg == 11 || alg == 12 || alg == 13) {
#ifdef SHA3_512
SHA3_CTX sha3_context;
if (alg == 10) {
SHA3Init(SHA3_224, &sha3_context);
result_msgcnt = SHA224_DIGEST_LENGTH;
} else if (alg == 11) {
SHA3Init(SHA3_256, &sha3_context);
result_msgcnt = SHA256_DIGEST_LENGTH;
} else if (alg == 12) {
SHA3Init(SHA3_384, &sha3_context);
result_msgcnt = SHA384_DIGEST_LENGTH;
} else if (alg == 13) {
SHA3Init(SHA3_512, &sha3_context);
result_msgcnt = SHA512_DIGEST_LENGTH;
}
hex_output = malloc(result_msgcnt * 2 + 1);
while (readcount = fread(auth_buffer, 1, BUF_SIZE, fp)) {
offset += readcount;
SHA3Update(&sha3_context, auth_buffer, readcount);
}
SHA3Final(auth_buffer, &sha3_context);
#endif
} else {
fprintf(stderr, "illegal alg operation: %d", alg);
return -1;
}
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
root@wl ~ # ./digest_solaris -a sha256 /bin/ls
a16a54349bfe0f9310c55cfb46695872658a7d50af1d6f4f2b5d33c109c888d7 sha256 60056
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
솔라리스 11부터는 SHA1 핑커프린트도 지원하고, IPS에서 검색할 수 있다.pkg search
명령에서, -f
옵션을 사용하면 된다. 예를 들어, 아래와 같이 확인해볼 수 있다.
root@wl ~ # digest -a sha1 /usr/bin/vim
f2495fa19fcc4b8a403e0bd4fef809d031296c68
root@wl ~ # pkg search -f f2495fa19fcc4b8a403e0bd4fef809d031296c68
INDEX ACTION VALUE PACKAGE
f2495fa19fcc4b8a403e0bd4fef809d031296c68 file usr/bin/vim pkg:/editor/vim/vim-core@7.3.254-0.175.0.0.0.2.537
root@wl ~ # pkg verify -v vim-core
PACKAGE STATUS
pkg://solaris/editor/vim/vim-core OK
컴파일러 옵션에 따른 성능
최적화된 옵션
컴파일시 -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