Message Digest, FingerPrint - 윈디하나의 솔라나라

목차

개요

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 커맨드

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

컴파일러 옵션에 따른 성능

  1. 최적화된 옵션

    컴파일시 -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
    
  2. 솔라리스 10 기본 소프트웨어

    참고삼아 솔라리스 10에 들어있는 기본 툴에 대해 재어보았다.
    root@wl ~ # time digest -a sha1 4GFile
    1bf99ee9f374e58e201e4dda4f474e570eb77229
    
    real    1m0.908s
    user    0m25.883s
    sys     0m13.137s
    
RSS ATOM XHTML 5 CSS3