hashTest.c   [plain text]


/* Copyright (c) 1998,2003-2006,2008 Apple Inc.
 *
 * hashTest.c - test CDSA digest functions.
 *
 * Revision History
 * ----------------
 *   4 May 2000  Doug Mitchell
 *		Ported to X/CDSA2. 
 *  12 May 1998	Doug Mitchell at Apple
 *		Created.
 */
/*
 * text size =       {random, from 100 bytes to 100k, in
 *                   geometrical steps, i.e. the number of
 *                   bytes would be 10^r, where r is random out of
 *                   {2,3,4,5,6}, plus a random integer in {0,..99}};
 *
 * for loop_count
 *     text contents = {random data, random size as specified above};
 *     generate digest in one shot;
 *	   generate digest with multiple random-sized updates;
 *     verify digests compare;
 *     for various bytes of text {
 *        corrupt text byte;
 *		  generate digest in one shot;
 *		  veridy digest is different;
 *        restore corrupted byte;
 *     }
 *  }
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <Security/cssm.h>
#include "cspwrap.h"
#include <Security/cssm.h>
#include <Security/cssmapple.h>
#include "cspwrap.h"
#include "common.h"

/*
 * Defaults.
 */
#define LOOPS_DEF		50
#define MIN_EXP			2		/* for data size 10**exp */
#define DEFAULT_MAX_EXP	3
#define MAX_EXP			5
#define INCR_DEFAULT	0		/* munge every incr bytes - zero means
								 * "adjust per ptext size" */
typedef enum {
	ALG_MD2 = 1,
	ALG_MD5,
	ALG_SHA1,
	ALG_SHA224,
	ALG_SHA256,
	ALG_SHA384,
	ALG_SHA512
};

#define ALG_FIRST		ALG_MD2
#define ALG_LAST		ALG_SHA512
#define MAX_DATA_SIZE	(100000 + 100)	/* bytes */
#define LOOP_NOTIFY		20

static void usage(char **argv)
{
	printf("usage: %s [options]\n", argv[0]);
	printf("   Options:\n");
	printf("   a=algorithm (s=SHA1; m=MD5; M=MD2; 4=SHA224; 2=SHA256; 3=SHA384; 5=SHA512; "
					"default=all\n");
	printf("   l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
	printf("   n=minExp (default=%d)\n", MIN_EXP);
	printf("   x=maxExp (default=%d, max=%d)\n", DEFAULT_MAX_EXP, MAX_EXP);
	printf("   i=increment (default=%d)\n", INCR_DEFAULT);
	printf("   p=pauseInterval (default=0, no pause)\n");
	printf("   z (zero data)\n");
	printf("   I (incrementing data)\n");
	printf("   g (good digest only)\n");
	printf("   D (CSP/DL; default = bare CSP)\n");
	printf("   v(erbose)\n");
	printf("   q(uiet)\n");
	printf("   h(elp)\n");
	exit(1);
}

#define LOG_FREQ	20

static int doTest(CSSM_CSP_HANDLE cspHand,
	uint32 alg,
	CSSM_DATA_PTR ptext,
	CSSM_BOOL verbose,
	CSSM_BOOL quiet,
	CSSM_BOOL mallocDigest,
	unsigned incr,
	CSSM_BOOL goodOnly)
{
	CSSM_DATA 		refDigest = {0, NULL};
	CSSM_DATA		testDigest = {0, NULL};
	unsigned		length;
	unsigned		byte;
	unsigned char	*data;
	unsigned char	origData;
	unsigned char	bits;
	int				rtn = 0;
	CSSM_RETURN		crtn;
	unsigned		loop = 0;
	
	/*
	 *     generate digest in one shot;
	 *	   generate digest with multiple random-sized updates;
	 *     verify digests compare;
	 *     for various bytes of ptext {
	 *        corrupt ptext byte;
	 *		  generate digest in one shot;
	 *		  verify digest is different;
	 *        restore corrupted byte;
	 *     }
	 */
	crtn = cspDigest(cspHand,
		alg,
		mallocDigest,
		ptext,
		&refDigest);
	if(crtn) {
		rtn = testError(quiet);
		goto abort;
	}
	crtn = cspStagedDigest(cspHand,
		alg,
		mallocDigest,
		CSSM_TRUE,			// multi updates
		ptext,
		&testDigest);
	if(crtn) {
		rtn = testError(quiet);
		goto abort;
	}
	if(refDigest.Length != testDigest.Length) {
		printf("Digest length mismatch (1)\n");
		rtn = testError(quiet);
		goto abort;
	}
	if(memcmp(refDigest.Data, testDigest.Data, refDigest.Length)) {
		printf("Digest miscompare (1)\n");
		rtn = testError(quiet);
		if(rtn) {
			goto abort;
		}
	}
	if(goodOnly) {
		rtn = 0;
		goto abort;
	}
	appFreeCssmData(&testDigest, CSSM_FALSE);
	testDigest.Length = 0;
	data = (unsigned char *)ptext->Data;
	length = ptext->Length;
	for(byte=0; byte<length; byte += incr) {
		if(verbose && ((loop++ % LOG_FREQ) == 0)) {
			printf("....byte %d\n", byte);
		}
		origData = data[byte];
		/*
		 * Generate random non-zero byte
		 */
		do {
			bits = genRand(1, 0xff) & 0xff;
		} while(bits == 0);
		data[byte] ^= bits;
		crtn = cspDigest(cspHand,
			alg,
			mallocDigest,
			ptext,
			&testDigest);
		if(crtn) {
			rtn = testError(quiet);
			break;
		}
		if(!memcmp(refDigest.Data, testDigest.Data, refDigest.Length)) {
			printf("Unexpected digest compare\n");
			rtn = testError(quiet);
			break;
		}
		appFreeCssmData(&testDigest, CSSM_FALSE);
		testDigest.Length = 0;
		data[byte] = origData;
	}
abort:
	/* free digests */
	if(refDigest.Length) {
		appFreeCssmData(&refDigest, CSSM_FALSE);
	}
	if(testDigest.Length) {
		appFreeCssmData(&testDigest, CSSM_FALSE);
	}
	return rtn;
}

int main(int argc, char **argv)
{
	int					arg;
	char				*argp;
	unsigned 			loop;
	CSSM_DATA			ptext;
	CSSM_CSP_HANDLE 	cspHand;
	CSSM_BOOL			mallocDigest;
	const char			*algStr;
	uint32				alg;		// ALG_MD5, etc.
	uint32				cssmAlg;	// CSSM_ALGID_MD5, etc.
	unsigned			actualIncr;
	int					i;
	
	/*
	 * User-spec'd params
	 */
	unsigned	loops = LOOPS_DEF;
	CSSM_BOOL	verbose = CSSM_FALSE;
	unsigned	minExp = MIN_EXP;
	unsigned	maxExp = DEFAULT_MAX_EXP;
	CSSM_BOOL	quiet = CSSM_FALSE;
	unsigned	incr = INCR_DEFAULT;
	unsigned	minAlg = ALG_FIRST;
	unsigned	maxAlg = ALG_LAST;
	unsigned	pauseInterval = 0;
	dataType 	dt;
	CSSM_BOOL	goodOnly = CSSM_FALSE;
	CSSM_BOOL	bareCsp = CSSM_TRUE;
	
	for(arg=1; arg<argc; arg++) {
		argp = argv[arg];
		switch(argp[0]) {
			case 'a':
				if(argp[1] != '=') {
					usage(argv);
				}
				switch(argp[2]) {
					case 's':
						minAlg = maxAlg = ALG_SHA1;
						break;
					case 'm':
						minAlg = maxAlg = ALG_MD5;
						break;
					case 'M':
						minAlg = maxAlg = ALG_MD2;
						break;
					case '4':
						minAlg = maxAlg = ALG_SHA224;
						break;
					case '2':
						minAlg = maxAlg = ALG_SHA256;
						break;
					case '3':
						minAlg = maxAlg = ALG_SHA384;
						break;
					case '5':
						minAlg = maxAlg = ALG_SHA512;
						break;
					default:
						usage(argv);
				}
				break;
		    case 'l':
				loops = atoi(&argp[2]);
				break;
		    case 'n':
				minExp = atoi(&argp[2]);
				break;
		    case 'x':
				maxExp = atoi(&argp[2]);
				if(maxExp > MAX_EXP) {
					usage(argv);
				}
				break;
		    case 'i':
				incr = atoi(&argp[2]);
				break;
		    case 'p':
		    	pauseInterval = atoi(&argp[2]);;
				break;
		    case 'v':
		    	verbose = CSSM_TRUE;
				break;
		    case 'q':
		    	quiet = CSSM_TRUE;
				break;
			case 'D':
				bareCsp = CSSM_FALSE;
				break;
			case 'z':
				dt = DT_Zero;
				break;
			case 'I':
				dt = DT_Increment;
				break;
			case 'g':
				goodOnly = CSSM_TRUE;
				break;
		    case 'h':
		    default:
				usage(argv);
		}
	}
	ptext.Data = (uint8 *)CSSM_MALLOC(MAX_DATA_SIZE);
	/* length set in test loop */
	if(ptext.Data == NULL) {
		printf("Insufficient heap\n");
		exit(1);
	}
	
	printf("Starting hashTest; args: ");
	for(i=1; i<argc; i++) {
		printf("%s ", argv[i]);
	}
	printf("\n");
	cspHand = cspDlDbStartup(bareCsp, NULL);
	if(cspHand == 0) {
		exit(1);
	}
	
	for(alg=minAlg; alg<=maxAlg; alg++) {
		switch(alg) {
			case ALG_MD5:
				algStr = "MD5";
				cssmAlg = CSSM_ALGID_MD5;
				break;
			case ALG_MD2:
				algStr = "MD2";
				cssmAlg = CSSM_ALGID_MD2;
				break;
			case ALG_SHA1:
				algStr = "SHA1";
				cssmAlg = CSSM_ALGID_SHA1;
				break;
			case ALG_SHA224:
				algStr = "SHA224";
				cssmAlg = CSSM_ALGID_SHA224;
				break;
			case ALG_SHA256:
				algStr = "SHA256";
				cssmAlg = CSSM_ALGID_SHA256;
				break;
			case ALG_SHA384:
				algStr = "SHA384";
				cssmAlg = CSSM_ALGID_SHA384;
				break;
			case ALG_SHA512:
				algStr = "SHA512";
				cssmAlg = CSSM_ALGID_SHA512;
				break;
		}
		if(!quiet) {
			printf("Testing alg %s\n", algStr);
		}
		for(loop=1; ; loop++) {
			ptext.Length = genData(ptext.Data, minExp, maxExp, dt);
			if(incr == 0) {
				/* adjust increment as appropriate */
				actualIncr = (ptext.Length / 50) + 1;
			}
			else {
				actualIncr = incr;
			}
			/* mix up mallocing */
			mallocDigest = (loop & 1) ? CSSM_TRUE : CSSM_FALSE;
			if(!quiet) {
			   	if(verbose || ((loop % LOOP_NOTIFY) == 0)) {
					printf("..loop %d text size %lu mallocDigest %d\n",
						loop, (unsigned long)ptext.Length, (int)mallocDigest);
				}
			}
			if(doTest(cspHand,
					cssmAlg,
					&ptext,
					verbose,
					quiet,
					mallocDigest,
					actualIncr,
					goodOnly)) {
				exit(1);
			}
			if(loops && (loop == loops)) {
				break;
			}
			if(pauseInterval && ((loop % pauseInterval) == 0)) {
				fpurge(stdin);
				printf("Hit CR to proceed: ");
				getchar();
			}
		}
	}
	if(!quiet) {
		printf("%s test complete\n", argv[0]);
	}
	return 0;
}