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;
  }
  