1/*
2 * macCompat.c - test compatibilty of two different implementations of a
3 * given MAC algorithm - one in the standard AppleCSP,
4 * one in BSAFE.
5 *
6 * Written by Doug Mitchell.
7 */
8
9#include <stdlib.h>
10#include <stdio.h>
11#include <time.h>
12#include <Security/cssm.h>
13#include <Security/cssmapple.h>
14#include "cspwrap.h"
15#include "common.h"
16#include "bsafeUtils.h"
17#include <string.h>
18#include "cspdlTesting.h"
19#include <openssl/hmac.h>
20
21/*
22 * Defaults.
23 */
24#define LOOPS_DEF		200
25#define MIN_EXP			2		/* for data size 10**exp */
26#define DEFAULT_MAX_EXP	4
27#define MAX_EXP			5
28
29#define MAX_DATA_SIZE	(100000 + 100)		/* bytes */
30#define MIN_KEY_SIZE	20					/* bytes - should be smaller */
31#define MAX_KEY_SIZE	64					/* bytes */
32#define LOOP_NOTIFY		20
33
34/*
35 * Enumerate algs our own way to allow iteration.
36 */
37#define ALG_MD5				1
38#define ALG_SHA1			2
39#define ALG_SHA1_LEGACY		3
40#define ALG_FIRST			ALG_MD5
41#define ALG_LAST			ALG_SHA1_LEGACY
42
43static void usage(char **argv)
44{
45	printf("usage: %s [options]\n", argv[0]);
46	printf("   Options:\n");
47	printf("   l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
48	printf("   n=minExp (default=%d)\n", MIN_EXP);
49	printf("   x=maxExp (default=%d, max=%d)\n", DEFAULT_MAX_EXP, MAX_EXP);
50	printf("   k=keySizeInBytes\n");
51	printf("   P=plainTextLen\n");
52	printf("   z (keys and plaintext all zeroes)\n");
53	printf("   p=pauseInterval (default=0, no pause)\n");
54	printf("   D (CSP/DL; default = bare CSP)\n");
55	printf("   v(erbose)\n");
56	printf("   q(uiet)\n");
57	printf("   h(elp)\n");
58	exit(1);
59}
60
61/*
62 * generate MAC using reference BSAFE with either one update
63 * (updateSizes == NULL) or specified set of update sizes.
64 */
65static CSSM_RETURN genMacBSAFE(
66	CSSM_ALGORITHMS		macAlg,
67	const CSSM_DATA		*key,				// raw key bytes
68	const CSSM_DATA		*inText,
69	unsigned			*updateSizes,		// NULL --> random updates
70											// else null-terminated list of sizes
71	CSSM_DATA_PTR 		outText)			// mallocd and returned
72{
73	CSSM_RETURN crtn;
74	BU_KEY buKey;
75
76	crtn = buGenSymKey(key->Length * 8, key, &buKey);
77	if(crtn) {
78		return crtn;
79	}
80	crtn = buGenMac(buKey,
81		macAlg,
82		inText,
83		updateSizes,
84		outText);
85	buFreeKey(buKey);
86	return crtn;
87}
88
89/*
90 * Produce HMACMD5 with openssl.
91 */
92static int doHmacMD5Ref(
93	const CSSM_DATA		*key,				// raw key bytes
94	const CSSM_DATA		*inText,
95	CSSM_DATA_PTR 		outText)			// mallocd and returned
96{
97	const EVP_MD *md = EVP_md5();
98	unsigned md_len = 16;
99	appSetupCssmData(outText, 16);
100	HMAC(md, key->Data, (int)key->Length,
101		inText->Data, inText->Length,
102		(unsigned char *)outText->Data, &md_len);
103	return 0;
104}
105
106/*
107 * Generate MAC, CSP, specified set of update sizes
108 */
109static CSSM_RETURN cspGenMacWithSizes(CSSM_CSP_HANDLE cspHand,
110		uint32 algorithm,
111		CSSM_KEY_PTR key,					// session key
112		const CSSM_DATA *text,
113		unsigned *updateSizes,				// null-terminated list of sizes
114		CSSM_DATA_PTR mac)					// RETURNED
115{
116	CSSM_CC_HANDLE	macHand;
117	CSSM_RETURN		crtn;
118	CSSM_DATA		currData = *text;
119
120	crtn = CSSM_CSP_CreateMacContext(cspHand,
121		algorithm,
122		key,
123		&macHand);
124	if(crtn) {
125		printError("CSSM_CSP_CreateMacContext", crtn);
126		return crtn;
127	}
128	crtn = CSSM_GenerateMacInit(macHand);
129	if(crtn) {
130		printError("CSSM_GenerateMacInit", crtn);
131		goto abort;
132	}
133	/* CSP mallocs */
134	mac->Data = NULL;
135	mac->Length = 0;
136
137	while(*updateSizes) {
138		currData.Length = *updateSizes;
139		crtn = CSSM_GenerateMacUpdate(macHand,
140			&currData,
141			1);
142		if(crtn) {
143			printError("CSSM_GenerateMacUpdate", crtn);
144			goto abort;
145		}
146		currData.Data += *updateSizes;
147		updateSizes++;
148	}
149	crtn = CSSM_GenerateMacFinal(macHand, mac);
150	if(crtn) {
151		printError("CSSM_GenerateMacFinal", crtn);
152	}
153abort:
154	crtn = CSSM_DeleteContext(macHand);
155	if(crtn) {
156		printError("CSSM_DeleteContext", crtn);
157	}
158	return crtn;
159}
160
161/*
162 * Verify MAC, CSP, specified set of update sizes
163 */
164static CSSM_RETURN cspVfyMacWithSizes(CSSM_CSP_HANDLE cspHand,
165		uint32 algorithm,
166		CSSM_KEY_PTR key,					// session key
167		const CSSM_DATA *text,
168		unsigned *updateSizes,				// null-terminated list of sizes
169		const CSSM_DATA *mac)
170{
171	CSSM_CC_HANDLE	macHand;
172	CSSM_RETURN		crtn;
173	CSSM_DATA		currData = *text;
174
175	crtn = CSSM_CSP_CreateMacContext(cspHand,
176		algorithm,
177		key,
178		&macHand);
179	if(crtn) {
180		printError("CSSM_CSP_CreateMacContext", crtn);
181		return crtn;
182	}
183	crtn = CSSM_VerifyMacInit(macHand);
184	if(crtn) {
185		printError("CSSM_VerifyMacInit", crtn);
186		goto abort;
187	}
188
189	while(*updateSizes) {
190		currData.Length = *updateSizes;
191		crtn = CSSM_VerifyMacUpdate(macHand,
192			&currData,
193			1);
194		if(crtn) {
195			printError("CSSM_GenerateMacUpdate", crtn);
196			goto abort;
197		}
198		currData.Data += *updateSizes;
199		updateSizes++;
200	}
201	crtn = CSSM_VerifyMacFinal(macHand, mac);
202	if(crtn) {
203		printError("CSSM_GenerateMacFinal", crtn);
204	}
205abort:
206	crtn = CSSM_DeleteContext(macHand);
207	if(crtn) {
208		printError("CSSM_DeleteContext", crtn);
209	}
210	return crtn;
211}
212
213/*
214 * Generate or verify MAC using CSP with either random-sized staged updates
215 * (updateSizes == NULL) or specified set of update sizes.
216 */
217static CSSM_RETURN genMacCSSM(
218	CSSM_CSP_HANDLE		cspHand,
219	CSSM_ALGORITHMS		macAlg,
220	CSSM_ALGORITHMS		keyAlg,
221	CSSM_BOOL			doGen,
222	const CSSM_DATA		*key,				// raw key bytes
223	CSSM_BOOL			genRaw,				// first generate raw key (CSPDL)
224	const CSSM_DATA		*inText,
225	unsigned			*updateSizes,		// NULL --> random updates
226											// else null-terminated list of sizes
227	CSSM_DATA_PTR 		outText)			// mallocd and returned if doGen
228{
229	CSSM_KEY_PTR		symKey;
230	CSSM_KEY			refKey;				// in case of genRaw
231	CSSM_BOOL			refKeyGenerated = CSSM_FALSE;
232	CSSM_RETURN			crtn;
233
234	if(genRaw) {
235		crtn = cspGenSymKeyWithBits(cspHand,
236			keyAlg,
237			CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY,
238			key,
239			key->Length,
240			&refKey);
241		if(crtn) {
242			return crtn;
243		}
244		symKey = &refKey;
245		refKeyGenerated = CSSM_TRUE;
246	}
247	else {
248		/* cook up a raw symmetric key */
249		symKey = cspGenSymKey(cspHand,
250			keyAlg,
251			"noLabel",
252			8,
253			CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY,
254			key->Length * 8,
255			CSSM_FALSE);			// ref key
256		if(symKey == NULL) {
257			return CSSM_ERRCODE_INTERNAL_ERROR;
258		}
259		if(symKey->KeyData.Length != key->Length) {
260			printf("***Generated key size error (exp %lu, got %lu)\n",
261				key->Length, symKey->KeyData.Length);
262			return CSSM_ERRCODE_INTERNAL_ERROR;
263		}
264		memmove(symKey->KeyData.Data, key->Data, key->Length);
265	}
266	if(doGen) {
267		/* CSP mallocs */
268		outText->Data = NULL;
269		outText->Length = 0;
270	}
271
272	/* go for it */
273	if(doGen) {
274		if(updateSizes) {
275			crtn = cspGenMacWithSizes(cspHand,
276				macAlg,
277				symKey,
278				inText,
279				updateSizes,
280				outText);
281		}
282		else {
283			crtn = cspStagedGenMac(cspHand,
284				macAlg,
285				symKey,
286				inText,
287				CSSM_TRUE,		// multiUpdates
288				CSSM_FALSE,		// mallocMac
289				outText);
290		}
291	}
292	else {
293		if(updateSizes) {
294			crtn = cspVfyMacWithSizes(cspHand,
295				macAlg,
296				symKey,
297				inText,
298				updateSizes,
299				outText);
300		}
301		else {
302			crtn = cspMacVerify(cspHand,
303				macAlg,
304				symKey,
305				inText,
306				outText,
307				CSSM_OK);
308		}
309	}
310	cspFreeKey(cspHand, symKey);
311	if(!refKeyGenerated) {
312		/* key itself mallocd by cspGenSymKey */
313		CSSM_FREE(symKey);
314	}
315	return crtn;
316}
317
318#define LOG_FREQ			20
319#define MAX_FIXED_UPDATES	5
320
321static int doTest(CSSM_CSP_HANDLE cspHand,
322	const CSSM_DATA		*ptext,
323	const CSSM_DATA		*keyData,
324	CSSM_BOOL			genRaw,				// first generate raw key (CSPDL)
325	uint32 				macAlg,
326	uint32 				keyAlg,
327	CSSM_BOOL			fixedUpdates,		// for testing CSSM_ALGID_SHA1HMAC_LEGACY
328	CSSM_BOOL 			quiet)
329{
330	CSSM_DATA 		macRef = {0, NULL};			// MAC, BSAFE reference
331	CSSM_DATA		macTest = {0, NULL};		// MAC, CSP test
332	int				rtn = 0;
333	CSSM_RETURN		crtn;
334	unsigned		updateSizes[MAX_FIXED_UPDATES+1];
335	unsigned		*updateSizesPtr;
336
337	if(fixedUpdates) {
338		/* calculate up to MAX_FIXED_UPDATES update sizes which add up to
339		 * ptext->Length */
340		int i;
341		unsigned bytesToGo = ptext->Length;
342
343		memset(updateSizes, 0, sizeof(unsigned) * (MAX_FIXED_UPDATES+1));
344		for(i=0; i<MAX_FIXED_UPDATES; i++) {
345			updateSizes[i] = genRand(1, bytesToGo);
346			bytesToGo -= updateSizes[i];
347			if(bytesToGo == 0) {
348				break;
349			}
350		}
351		updateSizesPtr = updateSizes;
352	}
353	else {
354		/*
355		 * CSP : random updates
356		 * BSAFE, openssl: single one-shot update
357		 */
358		updateSizesPtr = NULL;
359	}
360	/*
361	 * generate with each method;
362	 * verify MACs compare;
363	 * verify with test code;
364	 */
365	if(macAlg == CSSM_ALGID_MD5HMAC) {
366		doHmacMD5Ref(keyData, ptext, &macRef);
367		crtn = CSSM_OK;
368	}
369	else {
370		crtn = genMacBSAFE(macAlg,
371			keyData,
372			ptext,
373			updateSizesPtr,
374			&macRef);
375	}
376	if(crtn) {
377		return testError(quiet);
378	}
379	crtn = genMacCSSM(cspHand,
380		macAlg,
381		keyAlg,
382		CSSM_TRUE,
383		keyData,
384		genRaw,
385		ptext,
386		updateSizesPtr,
387		&macTest);
388	if(crtn) {
389		return testError(quiet);
390	}
391
392	/* ensure both methods resulted in same MAC */
393	if(macRef.Length != macTest.Length) {
394		printf("MAC length mismatch (1)\n");
395		rtn = testError(quiet);
396		if(rtn) {
397			goto abort;
398		}
399	}
400	if(memcmp(macRef.Data, macTest.Data, macTest.Length)) {
401		printf("MAC miscompare\n");
402		rtn = testError(quiet);
403		if(rtn) {
404			goto abort;
405		}
406	}
407
408	/* verify with the test method */
409	crtn = genMacCSSM(cspHand,
410		macAlg,
411		keyAlg,
412		CSSM_FALSE,
413		keyData,
414		genRaw,
415		ptext,
416		updateSizesPtr,
417		&macTest);
418	if(crtn) {
419		printf("***Unexpected MAC verify failure\n");
420		rtn = testError(quiet);
421	}
422	else {
423		rtn = 0;
424	}
425abort:
426	if(macTest.Length) {
427		CSSM_FREE(macTest.Data);
428	}
429	if(macRef.Length) {
430		CSSM_FREE(macRef.Data);
431	}
432	return rtn;
433}
434
435
436int main(int argc, char **argv)
437{
438	int					arg;
439	char				*argp;
440	unsigned			loop;
441	CSSM_DATA			ptext;
442	CSSM_CSP_HANDLE 	cspHand;
443	const char			*algStr;
444	uint32				macAlg;			// CSSM_ALGID_xxx
445	uint32				keyAlg;			// CSSM_ALGID_xxx
446	int					i;
447	unsigned			currAlg;		// ALG_xxx
448	int					rtn = 0;
449	CSSM_DATA			keyData;
450	CSSM_BOOL			genRaw = CSSM_FALSE;	// first generate raw key (CSPDL)
451
452	/*
453	 * User-spec'd params
454	 */
455	unsigned	minAlg = ALG_FIRST;
456	unsigned	maxAlg = ALG_LAST;
457	unsigned	loops = LOOPS_DEF;
458	CSSM_BOOL	verbose = CSSM_FALSE;
459	unsigned	minExp = MIN_EXP;
460	unsigned	maxExp = DEFAULT_MAX_EXP;
461	CSSM_BOOL	quiet = CSSM_FALSE;
462	unsigned	pauseInterval = 0;
463	CSSM_BOOL	bareCsp = CSSM_TRUE;
464	CSSM_BOOL	fixedUpdates;
465	CSSM_BOOL	allZeroes = CSSM_FALSE;
466	unsigned	keySizeSpecd = 0;
467	unsigned	ptextLenSpecd = 0;
468
469	for(arg=1; arg<argc; arg++) {
470		argp = argv[arg];
471		switch(argp[0]) {
472		    case 'l':
473				loops = atoi(&argp[2]);
474				break;
475		    case 'n':
476				minExp = atoi(&argp[2]);
477				break;
478		    case 'x':
479				maxExp = atoi(&argp[2]);
480				if(maxExp > MAX_EXP) {
481					usage(argv);
482				}
483				break;
484		    case 'v':
485		    	verbose = CSSM_TRUE;
486				break;
487			case 'D':
488				bareCsp = CSSM_FALSE;
489				#if CSPDL_ALL_KEYS_ARE_REF
490				genRaw = CSSM_TRUE;
491				#endif
492				break;
493		    case 'q':
494		    	quiet = CSSM_TRUE;
495				break;
496		    case 'p':
497		    	pauseInterval = atoi(&argp[2]);
498				break;
499			case 'z':
500				allZeroes = CSSM_TRUE;
501				break;
502			case 'k':
503				keySizeSpecd = atoi(&argp[2]);
504				break;
505			case 'P':
506				ptextLenSpecd = atoi(&argp[2]);
507				break;
508		    case 'h':
509		    default:
510				usage(argv);
511		}
512	}
513	if(minExp > maxExp) {
514		printf("***minExp must be <= maxExp\n");
515		usage(argv);
516	}
517	ptext.Data = (uint8 *)CSSM_MALLOC(MAX_DATA_SIZE);
518	if(ptext.Data == NULL) {
519		printf("Insufficient heap space\n");
520		exit(1);
521	}
522	/* ptext length set in test loop */
523
524	keyData.Data = (uint8 *)CSSM_MALLOC(MAX_KEY_SIZE);
525	if(keyData.Data == NULL) {
526		printf("Insufficient heap space\n");
527		exit(1);
528	}
529	/* key length set in test loop */
530
531	printf("Starting macCompat; args: ");
532	for(i=1; i<argc; i++) {
533		printf("%s ", argv[i]);
534	}
535	printf("\n");
536	cspHand = cspDlDbStartup(bareCsp, NULL);
537	if(cspHand == 0) {
538		exit(1);
539	}
540	if(pauseInterval) {
541		fpurge(stdin);
542		printf("Top of test; hit CR to proceed: ");
543		getchar();
544	}
545	for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
546		if((currAlg == ALG_SHA1_LEGACY) && !bareCsp && !CSPDL_SHA1HMAC_LEGACY_ENABLE) {
547			continue;
548		}
549
550		/* some default values... */
551		switch(currAlg) {
552			case ALG_MD5:
553				macAlg = CSSM_ALGID_MD5HMAC;
554				keyAlg = CSSM_ALGID_MD5HMAC;
555				algStr = "MD5";
556				fixedUpdates = CSSM_FALSE;
557				break;
558			case ALG_SHA1:
559				macAlg = CSSM_ALGID_SHA1HMAC;
560				keyAlg = CSSM_ALGID_SHA1HMAC;
561				algStr = "SHA1";
562				fixedUpdates = CSSM_FALSE;
563				break;
564			case ALG_SHA1_LEGACY:
565				macAlg = CSSM_ALGID_SHA1HMAC_LEGACY;
566				keyAlg = CSSM_ALGID_SHA1HMAC;
567				algStr = "SHA1_LEGACY";
568				fixedUpdates = CSSM_TRUE;
569				break;
570			default:
571				printf("***Brrzap. Bad alg.\n");
572				exit(1);
573		}
574
575		if(!quiet || verbose) {
576			printf("Testing alg %s\n", algStr);
577		}
578		for(loop=1; ; loop++) {
579			/* random ptext and key */
580			ptext.Length = genData(ptext.Data, minExp, maxExp, DT_Random);
581			if(ptextLenSpecd) {
582				ptext.Length = ptextLenSpecd;
583			}
584			if(allZeroes) {
585				memset(ptext.Data, 0, ptext.Length);
586			}
587			if(macAlg == CSSM_ALGID_SHA1HMAC_LEGACY) {
588				simpleGenData(&keyData, 20, 20);
589			}
590			else {
591				simpleGenData(&keyData, MIN_KEY_SIZE, MAX_KEY_SIZE);
592				if(keySizeSpecd) {
593					keyData.Length = keySizeSpecd;
594				}
595			}
596			if(allZeroes) {
597				memset(keyData.Data, 0, keyData.Length);
598			}
599			if(!quiet) {
600			   	if(verbose || ((loop % LOOP_NOTIFY) == 0)) {
601					printf("..loop %d text size %lu keySize %lu\n",
602						loop, ptext.Length, keyData.Length);
603				}
604			}
605
606			if(doTest(cspHand,
607					&ptext,
608					&keyData,
609					genRaw,
610					macAlg,
611					keyAlg,
612					fixedUpdates,
613					quiet)) {
614				rtn = 1;
615				break;
616			}
617			if(pauseInterval && ((loop % pauseInterval) == 0)) {
618				char c;
619				fpurge(stdin);
620				printf("Hit CR to proceed, q to abort: ");
621				c = getchar();
622				if(c == 'q') {
623					goto testDone;
624				}
625			}
626			if(loops && (loop == loops)) {
627				break;
628			}
629		}	/* main loop */
630		if(rtn) {
631			break;
632		}
633
634	}	/* for algs */
635
636testDone:
637	cspShutdown(cspHand, bareCsp);
638	if(pauseInterval) {
639		fpurge(stdin);
640		printf("ModuleDetach/Unload complete; hit CR to exit: ");
641		getchar();
642	}
643	if((rtn == 0) && !quiet) {
644		printf("%s test complete\n", argv[0]);
645	}
646	CSSM_FREE(ptext.Data);
647	CSSM_FREE(keyData.Data);
648	return rtn;
649}
650
651
652