1/* Copyright (c) 1998,2003-2006,2008 Apple Inc.
2 *
3 * hashTest.c - test CDSA digest functions.
4 *
5 * Revision History
6 * ----------------
7 *   4 May 2000  Doug Mitchell
8 *		Ported to X/CDSA2.
9 *  12 May 1998	Doug Mitchell at Apple
10 *		Created.
11 */
12/*
13 * text size =       {random, from 100 bytes to 100k, in
14 *                   geometrical steps, i.e. the number of
15 *                   bytes would be 10^r, where r is random out of
16 *                   {2,3,4,5,6}, plus a random integer in {0,..99}};
17 *
18 * for loop_count
19 *     text contents = {random data, random size as specified above};
20 *     generate digest in one shot;
21 *	   generate digest with multiple random-sized updates;
22 *     verify digests compare;
23 *     for various bytes of text {
24 *        corrupt text byte;
25 *		  generate digest in one shot;
26 *		  veridy digest is different;
27 *        restore corrupted byte;
28 *     }
29 *  }
30 */
31
32#include <stdlib.h>
33#include <stdio.h>
34#include <time.h>
35#include <string.h>
36#include <Security/cssm.h>
37#include "cspwrap.h"
38#include <Security/cssm.h>
39#include <Security/cssmapple.h>
40#include "cspwrap.h"
41#include "common.h"
42
43/*
44 * Defaults.
45 */
46#define LOOPS_DEF		50
47#define MIN_EXP			2		/* for data size 10**exp */
48#define DEFAULT_MAX_EXP	3
49#define MAX_EXP			5
50#define INCR_DEFAULT	0		/* munge every incr bytes - zero means
51								 * "adjust per ptext size" */
52typedef enum {
53	ALG_MD2 = 1,
54	ALG_MD5,
55	ALG_SHA1,
56	ALG_SHA224,
57	ALG_SHA256,
58	ALG_SHA384,
59	ALG_SHA512
60};
61
62#define ALG_FIRST		ALG_MD2
63#define ALG_LAST		ALG_SHA512
64#define MAX_DATA_SIZE	(100000 + 100)	/* bytes */
65#define LOOP_NOTIFY		20
66
67static void usage(char **argv)
68{
69	printf("usage: %s [options]\n", argv[0]);
70	printf("   Options:\n");
71	printf("   a=algorithm (s=SHA1; m=MD5; M=MD2; 4=SHA224; 2=SHA256; 3=SHA384; 5=SHA512; "
72					"default=all\n");
73	printf("   l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
74	printf("   n=minExp (default=%d)\n", MIN_EXP);
75	printf("   x=maxExp (default=%d, max=%d)\n", DEFAULT_MAX_EXP, MAX_EXP);
76	printf("   i=increment (default=%d)\n", INCR_DEFAULT);
77	printf("   p=pauseInterval (default=0, no pause)\n");
78	printf("   z (zero data)\n");
79	printf("   I (incrementing data)\n");
80	printf("   g (good digest only)\n");
81	printf("   D (CSP/DL; default = bare CSP)\n");
82	printf("   v(erbose)\n");
83	printf("   q(uiet)\n");
84	printf("   h(elp)\n");
85	exit(1);
86}
87
88#define LOG_FREQ	20
89
90static int doTest(CSSM_CSP_HANDLE cspHand,
91	uint32 alg,
92	CSSM_DATA_PTR ptext,
93	CSSM_BOOL verbose,
94	CSSM_BOOL quiet,
95	CSSM_BOOL mallocDigest,
96	unsigned incr,
97	CSSM_BOOL goodOnly)
98{
99	CSSM_DATA 		refDigest = {0, NULL};
100	CSSM_DATA		testDigest = {0, NULL};
101	unsigned		length;
102	unsigned		byte;
103	unsigned char	*data;
104	unsigned char	origData;
105	unsigned char	bits;
106	int				rtn = 0;
107	CSSM_RETURN		crtn;
108	unsigned		loop = 0;
109
110	/*
111	 *     generate digest in one shot;
112	 *	   generate digest with multiple random-sized updates;
113	 *     verify digests compare;
114	 *     for various bytes of ptext {
115	 *        corrupt ptext byte;
116	 *		  generate digest in one shot;
117	 *		  verify digest is different;
118	 *        restore corrupted byte;
119	 *     }
120	 */
121	crtn = cspDigest(cspHand,
122		alg,
123		mallocDigest,
124		ptext,
125		&refDigest);
126	if(crtn) {
127		rtn = testError(quiet);
128		goto abort;
129	}
130	crtn = cspStagedDigest(cspHand,
131		alg,
132		mallocDigest,
133		CSSM_TRUE,			// multi updates
134		ptext,
135		&testDigest);
136	if(crtn) {
137		rtn = testError(quiet);
138		goto abort;
139	}
140	if(refDigest.Length != testDigest.Length) {
141		printf("Digest length mismatch (1)\n");
142		rtn = testError(quiet);
143		goto abort;
144	}
145	if(memcmp(refDigest.Data, testDigest.Data, refDigest.Length)) {
146		printf("Digest miscompare (1)\n");
147		rtn = testError(quiet);
148		if(rtn) {
149			goto abort;
150		}
151	}
152	if(goodOnly) {
153		rtn = 0;
154		goto abort;
155	}
156	appFreeCssmData(&testDigest, CSSM_FALSE);
157	testDigest.Length = 0;
158	data = (unsigned char *)ptext->Data;
159	length = ptext->Length;
160	for(byte=0; byte<length; byte += incr) {
161		if(verbose && ((loop++ % LOG_FREQ) == 0)) {
162			printf("....byte %d\n", byte);
163		}
164		origData = data[byte];
165		/*
166		 * Generate random non-zero byte
167		 */
168		do {
169			bits = genRand(1, 0xff) & 0xff;
170		} while(bits == 0);
171		data[byte] ^= bits;
172		crtn = cspDigest(cspHand,
173			alg,
174			mallocDigest,
175			ptext,
176			&testDigest);
177		if(crtn) {
178			rtn = testError(quiet);
179			break;
180		}
181		if(!memcmp(refDigest.Data, testDigest.Data, refDigest.Length)) {
182			printf("Unexpected digest compare\n");
183			rtn = testError(quiet);
184			break;
185		}
186		appFreeCssmData(&testDigest, CSSM_FALSE);
187		testDigest.Length = 0;
188		data[byte] = origData;
189	}
190abort:
191	/* free digests */
192	if(refDigest.Length) {
193		appFreeCssmData(&refDigest, CSSM_FALSE);
194	}
195	if(testDigest.Length) {
196		appFreeCssmData(&testDigest, CSSM_FALSE);
197	}
198	return rtn;
199}
200
201int main(int argc, char **argv)
202{
203	int					arg;
204	char				*argp;
205	unsigned 			loop;
206	CSSM_DATA			ptext;
207	CSSM_CSP_HANDLE 	cspHand;
208	CSSM_BOOL			mallocDigest;
209	const char			*algStr;
210	uint32				alg;		// ALG_MD5, etc.
211	uint32				cssmAlg;	// CSSM_ALGID_MD5, etc.
212	unsigned			actualIncr;
213	int					i;
214
215	/*
216	 * User-spec'd params
217	 */
218	unsigned	loops = LOOPS_DEF;
219	CSSM_BOOL	verbose = CSSM_FALSE;
220	unsigned	minExp = MIN_EXP;
221	unsigned	maxExp = DEFAULT_MAX_EXP;
222	CSSM_BOOL	quiet = CSSM_FALSE;
223	unsigned	incr = INCR_DEFAULT;
224	unsigned	minAlg = ALG_FIRST;
225	unsigned	maxAlg = ALG_LAST;
226	unsigned	pauseInterval = 0;
227	dataType 	dt;
228	CSSM_BOOL	goodOnly = CSSM_FALSE;
229	CSSM_BOOL	bareCsp = CSSM_TRUE;
230
231	for(arg=1; arg<argc; arg++) {
232		argp = argv[arg];
233		switch(argp[0]) {
234			case 'a':
235				if(argp[1] != '=') {
236					usage(argv);
237				}
238				switch(argp[2]) {
239					case 's':
240						minAlg = maxAlg = ALG_SHA1;
241						break;
242					case 'm':
243						minAlg = maxAlg = ALG_MD5;
244						break;
245					case 'M':
246						minAlg = maxAlg = ALG_MD2;
247						break;
248					case '4':
249						minAlg = maxAlg = ALG_SHA224;
250						break;
251					case '2':
252						minAlg = maxAlg = ALG_SHA256;
253						break;
254					case '3':
255						minAlg = maxAlg = ALG_SHA384;
256						break;
257					case '5':
258						minAlg = maxAlg = ALG_SHA512;
259						break;
260					default:
261						usage(argv);
262				}
263				break;
264		    case 'l':
265				loops = atoi(&argp[2]);
266				break;
267		    case 'n':
268				minExp = atoi(&argp[2]);
269				break;
270		    case 'x':
271				maxExp = atoi(&argp[2]);
272				if(maxExp > MAX_EXP) {
273					usage(argv);
274				}
275				break;
276		    case 'i':
277				incr = atoi(&argp[2]);
278				break;
279		    case 'p':
280		    	pauseInterval = atoi(&argp[2]);;
281				break;
282		    case 'v':
283		    	verbose = CSSM_TRUE;
284				break;
285		    case 'q':
286		    	quiet = CSSM_TRUE;
287				break;
288			case 'D':
289				bareCsp = CSSM_FALSE;
290				break;
291			case 'z':
292				dt = DT_Zero;
293				break;
294			case 'I':
295				dt = DT_Increment;
296				break;
297			case 'g':
298				goodOnly = CSSM_TRUE;
299				break;
300		    case 'h':
301		    default:
302				usage(argv);
303		}
304	}
305	ptext.Data = (uint8 *)CSSM_MALLOC(MAX_DATA_SIZE);
306	/* length set in test loop */
307	if(ptext.Data == NULL) {
308		printf("Insufficient heap\n");
309		exit(1);
310	}
311
312	printf("Starting hashTest; args: ");
313	for(i=1; i<argc; i++) {
314		printf("%s ", argv[i]);
315	}
316	printf("\n");
317	cspHand = cspDlDbStartup(bareCsp, NULL);
318	if(cspHand == 0) {
319		exit(1);
320	}
321
322	for(alg=minAlg; alg<=maxAlg; alg++) {
323		switch(alg) {
324			case ALG_MD5:
325				algStr = "MD5";
326				cssmAlg = CSSM_ALGID_MD5;
327				break;
328			case ALG_MD2:
329				algStr = "MD2";
330				cssmAlg = CSSM_ALGID_MD2;
331				break;
332			case ALG_SHA1:
333				algStr = "SHA1";
334				cssmAlg = CSSM_ALGID_SHA1;
335				break;
336			case ALG_SHA224:
337				algStr = "SHA224";
338				cssmAlg = CSSM_ALGID_SHA224;
339				break;
340			case ALG_SHA256:
341				algStr = "SHA256";
342				cssmAlg = CSSM_ALGID_SHA256;
343				break;
344			case ALG_SHA384:
345				algStr = "SHA384";
346				cssmAlg = CSSM_ALGID_SHA384;
347				break;
348			case ALG_SHA512:
349				algStr = "SHA512";
350				cssmAlg = CSSM_ALGID_SHA512;
351				break;
352		}
353		if(!quiet) {
354			printf("Testing alg %s\n", algStr);
355		}
356		for(loop=1; ; loop++) {
357			ptext.Length = genData(ptext.Data, minExp, maxExp, dt);
358			if(incr == 0) {
359				/* adjust increment as appropriate */
360				actualIncr = (ptext.Length / 50) + 1;
361			}
362			else {
363				actualIncr = incr;
364			}
365			/* mix up mallocing */
366			mallocDigest = (loop & 1) ? CSSM_TRUE : CSSM_FALSE;
367			if(!quiet) {
368			   	if(verbose || ((loop % LOOP_NOTIFY) == 0)) {
369					printf("..loop %d text size %lu mallocDigest %d\n",
370						loop, (unsigned long)ptext.Length, (int)mallocDigest);
371				}
372			}
373			if(doTest(cspHand,
374					cssmAlg,
375					&ptext,
376					verbose,
377					quiet,
378					mallocDigest,
379					actualIncr,
380					goodOnly)) {
381				exit(1);
382			}
383			if(loops && (loop == loops)) {
384				break;
385			}
386			if(pauseInterval && ((loop % pauseInterval) == 0)) {
387				fpurge(stdin);
388				printf("Hit CR to proceed: ");
389				getchar();
390			}
391		}
392	}
393	if(!quiet) {
394		printf("%s test complete\n", argv[0]);
395	}
396	return 0;
397}
398