1/* Copyright (c) 1997,2003-2005,2008 Apple Inc.
2 *
3 * badmac.c - Verify bad MAC detect.
4 *
5 * Revision History
6 * ----------------
7 *   4 May 2000  Doug Mitchell
8 *		Ported to X/CDSA2.
9 *  21 Dec 1998	Doug Mitchell at Apple
10 *		Created.
11 */
12/*
13 * text size =       {random, from 100 bytes to 1 megabyte, 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 random MAC key;
21 *     generate MAC, validate;
22 *     for various bytes of ptext {
23 *        corrupt text byte;
24 *        verify bad MAC;
25 *        restore corrupted byte;
26 *     }
27 *  }
28 */
29
30#include <stdlib.h>
31#include <stdio.h>
32#include <time.h>
33#include <string.h>
34#include <Security/cssm.h>
35#include "cspwrap.h"
36#include "common.h"
37#include "cspdlTesting.h"
38
39#define USAGE_NAME		"noUsage"
40#define USAGE_NAME_LEN	(strlen(USAGE_NAME))
41
42/*
43 * HMAC/SHA1 can not do multiple updates with BSAFE (though the CSP's current
44 * internal implementation can.)
45 * Fixed in Puma; the bug was in BSAFE.
46 */
47#define HMACSHA_MULTI_UPDATES	1
48
49/*
50 * Defaults.
51 */
52#define LOOPS_DEF			10
53#define MIN_EXP				2		/* for data size 10**exp */
54#define DEFAULT_MAX_EXP		2
55#define MAX_EXP				5
56#define INCR_DEFAULT		0		/* munge every incr bytes - zero means
57									 * "adjust per ptext size" */
58
59/*
60 * Enumerate algs our own way to allow iteration.
61 */
62#define ALG_SHA1HMAC		1
63#define ALG_MD5HMAC			2
64#define ALG_FIRST			ALG_SHA1HMAC
65#define ALG_LAST			ALG_MD5HMAC
66#define MAX_DATA_SIZE		(100000 + 100)	/* bytes */
67
68static void usage(char **argv)
69{
70	printf("usage: %s [options]\n", argv[0]);
71	printf("   Options:\n");
72	printf("   l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
73	printf("   n=minExp (default=%d)\n", MIN_EXP);
74	printf("   x=maxExp (default=%d, max=%d)\n", DEFAULT_MAX_EXP, MAX_EXP);
75	printf("   i=increment (default=%d)\n", INCR_DEFAULT);
76	printf("   r(eference keys only)\n");
77	printf("   m (CSM mallocs MAC)\n");
78	printf("   p=pauseInterval (default=0, no pause)\n");
79	printf("   D (CSP/DL; default = bare CSP)\n");
80	printf("   v(erbose)\n");
81	printf("   q(uiet)\n");
82	printf("   h(elp)\n");
83	exit(1);
84}
85
86#define LOG_FREQ	20
87static int doTest(CSSM_CSP_HANDLE cspHand,
88	uint32 macAlg,					// CSSM_ALGID_xxx mac algorithm
89	CSSM_DATA_PTR ptext,
90	CSSM_BOOL verbose,
91	CSSM_BOOL quiet,
92	unsigned keySize,
93	unsigned incr,
94	CSSM_BOOL stagedGen,
95	CSSM_BOOL stagedVerify,
96	CSSM_BOOL mallocMac,
97	CSSM_BOOL refKey)
98{
99	CSSM_KEY_PTR	symmKey;
100	CSSM_DATA 		mac = {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	uint32			keyGenAlg;
109	unsigned		loop = 0;
110
111	switch(macAlg) {
112		case CSSM_ALGID_SHA1HMAC:
113			keyGenAlg = CSSM_ALGID_SHA1HMAC;
114			break;
115		case CSSM_ALGID_MD5HMAC:
116			keyGenAlg = CSSM_ALGID_MD5HMAC;
117			break;
118		default:
119			printf("bogus algorithm\n");
120			return 1;
121	}
122	symmKey = cspGenSymKey(cspHand,
123		keyGenAlg,
124		"noLabel",
125		7,
126		CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY,
127		keySize,
128		refKey);
129	if(symmKey == NULL) {
130		rtn = testError(quiet);
131		goto abort;
132	}
133	if(stagedGen) {
134		crtn = cspStagedGenMac(cspHand,
135			macAlg,
136			symmKey,
137			ptext,
138			mallocMac,
139			CSSM_TRUE,			// multi
140			&mac);
141	}
142	else {
143		crtn = cspGenMac(cspHand,
144			macAlg,
145			symmKey,
146			ptext,
147			&mac);
148	}
149	if(crtn) {
150		rtn = 1;
151		goto abort;
152	}
153	if(stagedVerify) {
154		crtn = cspStagedMacVerify(cspHand,
155			macAlg,
156			symmKey,
157			ptext,
158			&mac,
159			CSSM_TRUE,			// multi
160			CSSM_OK);			// expectedResult
161	}
162	else {
163		crtn = cspMacVerify(cspHand,
164			macAlg,
165			symmKey,
166			ptext,
167			&mac,
168			CSSM_OK);
169	}
170	if(crtn) {
171		printf("**Unexpected BAD MAC\n");
172		return testError(quiet);
173	}
174	data = (unsigned char *)ptext->Data;
175	length = ptext->Length;
176	for(byte=0; byte<length; byte += incr) {
177		if(verbose && ((loop++ % LOG_FREQ) == 0)) {
178			printf("  ..byte %d\n", byte);
179		}
180		origData = data[byte];
181		/*
182		 * Generate random non-zero byte
183		 */
184		do {
185			bits = genRand(1, 0xff) & 0xff;
186		} while(bits == 0);
187		data[byte] ^= bits;
188		if(stagedVerify) {
189			crtn = cspStagedMacVerify(cspHand,
190				macAlg,
191				symmKey,
192				ptext,
193				&mac,
194				CSSM_TRUE,					// multi
195				CSSMERR_CSP_VERIFY_FAILED);	// expect failure
196		}
197		else {
198			crtn = cspMacVerify(cspHand,
199				macAlg,
200				symmKey,
201				ptext,
202				&mac,
203				CSSMERR_CSP_VERIFY_FAILED);
204		}
205		if(crtn) {
206			return testError(quiet);
207		}
208		data[byte] = origData;
209	}
210abort:
211	/* free key */
212	if(cspFreeKey(cspHand, symmKey)) {
213		printf("Error freeing symmKey\n");
214		rtn = 1;
215	}
216	CSSM_FREE(mac.Data);
217	return rtn;
218}
219
220int main(int argc, char **argv)
221{
222	int					arg;
223	char				*argp;
224	unsigned			loop;
225	CSSM_DATA			ptext;
226	CSSM_CSP_HANDLE 	CSPHandle;
227	CSSM_BOOL			stagedSign;
228	CSSM_BOOL 			stagedVfy;
229	CSSM_BOOL 			mallocMac;
230	CSSM_BOOL 			refKey;
231	const char 			*algStr;
232	unsigned			actualIncr;
233	uint32				macAlg;			// CSSM_ALGID_xxx
234	unsigned			currAlg;		// ALG_xxx
235	int					i;
236	int 				rtn = 0;
237
238	/*
239	 * User-spec'd params
240	 */
241	unsigned	loops = LOOPS_DEF;
242	CSSM_BOOL	verbose = CSSM_FALSE;
243	unsigned	minExp = MIN_EXP;
244	unsigned	maxExp = DEFAULT_MAX_EXP;
245	CSSM_BOOL	quiet = CSSM_FALSE;
246	unsigned	keySizeInBits = CSP_KEY_SIZE_DEFAULT;
247	unsigned	incr = INCR_DEFAULT;
248	unsigned	minAlg = ALG_FIRST;
249	uint32		maxAlg = ALG_LAST;
250	unsigned	pauseInterval = 0;
251	CSSM_BOOL	bareCsp = CSSM_TRUE;
252	CSSM_BOOL	refKeysOnly = CSSM_FALSE;
253	CSSM_BOOL	cspMallocs = CSSM_FALSE;
254
255	#if	macintosh
256	argc = ccommand(&argv);
257	#endif
258	for(arg=1; arg<argc; arg++) {
259		argp = argv[arg];
260		switch(argp[0]) {
261			/* no Alg or keySizeInBits spec for now */
262		    case 'l':
263				loops = atoi(&argp[2]);
264				break;
265		    case 'n':
266				minExp = atoi(&argp[2]);
267				break;
268		    case 'x':
269				maxExp = atoi(&argp[2]);
270				if(maxExp > MAX_EXP) {
271					usage(argv);
272				}
273				break;
274		    case 'i':
275				incr = atoi(&argp[2]);
276				break;
277		    case 'p':
278				pauseInterval = atoi(&argp[2]);
279				break;
280			case 'D':
281				bareCsp = CSSM_FALSE;
282				#if CSPDL_ALL_KEYS_ARE_REF
283				refKeysOnly = CSSM_TRUE;
284				#endif
285				break;
286			case 'r':
287				refKeysOnly = CSSM_TRUE;
288				break;
289			case 'm':
290				cspMallocs = CSSM_TRUE;
291				break;
292		    case 'v':
293		    	verbose = CSSM_TRUE;
294				break;
295		    case 'q':
296		    	quiet = CSSM_TRUE;
297				break;
298		    case 'h':
299		    default:
300				usage(argv);
301		}
302	}
303	ptext.Data = (uint8 *)CSSM_MALLOC(MAX_DATA_SIZE);
304	/* length set in test loop */
305	if(ptext.Data == NULL) {
306		printf("Insufficient heap\n");
307		exit(1);
308	}
309	printf("Starting badmac; args: ");
310	for(i=1; i<argc; i++) {
311		printf("%s ", argv[i]);
312	}
313	printf("\n");
314	CSPHandle = cspDlDbStartup(bareCsp, NULL);
315	if(CSPHandle == 0) {
316		exit(1);
317	}
318	for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
319		switch(currAlg) {
320			case ALG_SHA1HMAC:
321				macAlg = CSSM_ALGID_SHA1HMAC;
322				algStr = "SHA1HMAC";
323				break;
324			case ALG_MD5HMAC:
325				macAlg = CSSM_ALGID_MD5HMAC;
326				algStr = "MD5HMAC";
327				break;
328		}
329		if(!quiet) {
330			printf("Testing alg %s\n", algStr);
331		}
332		for(loop=1; ; loop++) {
333			ptext.Length = genData(ptext.Data, minExp, maxExp, DT_Random);
334			if(!quiet) {
335				printf("..loop %d text size %lu\n", loop, ptext.Length);
336			}
337			if(incr == 0) {
338				/* adjust increment as appropriate */
339				actualIncr = (ptext.Length / 50) + 1;
340			}
341			else {
342				actualIncr = incr;
343			}
344			/* mix up staging & ref key format*/
345			stagedSign = (loop & 1) ? CSSM_TRUE : CSSM_FALSE;
346			stagedVfy  = (loop & 2) ? CSSM_TRUE : CSSM_FALSE;
347			if(refKeysOnly) {
348				refKey = CSSM_TRUE;
349			}
350			else {
351				refKey     = (loop & 4) ? CSSM_TRUE : CSSM_FALSE;
352			}
353			if(cspMallocs) {
354				mallocMac = CSSM_FALSE;
355			}
356			else {
357				mallocMac  = (loop & 8) ? CSSM_TRUE : CSSM_FALSE;
358			}
359
360			#if		!HMACSHA_MULTI_UPDATES
361			if(macAlg == CSSM_ALGID_SHA1HMAC) {
362				stagedSign = stagedVfy = CSSM_FALSE;
363			}
364			#endif	/* HMACSHA_MULTI_UPDATES */
365
366			if(!quiet) {
367				printf("  stagedSign %d  stagedVfy %d refKey %d mallocMac %d\n",
368					 (int)stagedSign, (int)stagedVfy, (int)refKey, (int)mallocMac);
369			}
370			if(doTest(CSPHandle,
371					macAlg,
372					&ptext,
373					verbose,
374					quiet,
375					keySizeInBits,
376					actualIncr,
377					stagedSign,
378					stagedVfy,
379					mallocMac,
380					refKey)) {
381				rtn = 1;
382				goto testDone;
383			}
384			if(loops && (loop == loops)) {
385				break;
386			}
387			if(pauseInterval && ((loop % pauseInterval) == 0)) {
388				fpurge(stdin);
389				printf("Hit CR to proceed: ");
390				getchar();
391			}
392		}	/* for loop */
393	}		/* for alg */
394testDone:
395	CSSM_ModuleDetach(CSPHandle);
396	if((rtn == 0) && !quiet) {
397		printf("%s test complete\n", argv[0]);
398	}
399	return rtn;
400}
401