1/*
2 * ccHmacClone - test CommonCrypto's clone context for HMAC.
3 *
4 * Written 3/30/2006 by Doug Mitchell.
5 */
6
7#include <stdlib.h>
8#include <stdio.h>
9#include <time.h>
10#include "common.h"
11#include <string.h>
12#include <CommonCrypto/CommonHMAC.h>
13
14/*
15 * Defaults.
16 */
17#define LOOPS_DEF		200
18
19#define MIN_DATA_SIZE	8
20#define MAX_DATA_SIZE	10000			/* bytes */
21#define MIN_KEY_SIZE	1
22#define MAX_KEY_SIZE	256				/* bytes */
23#define LOOP_NOTIFY		20
24
25/*
26 * Enumerate algs our own way to allow iteration.
27 */
28typedef enum {
29	ALG_MD5 = 1,
30	ALG_SHA1,
31	ALG_SHA224,
32	ALG_SHA256,
33	ALG_SHA384,
34	ALG_SHA512,
35} HmacAlg;
36#define ALG_FIRST			ALG_MD5
37#define ALG_LAST			ALG_SHA512
38
39#define LOG_SIZE			0
40#if		LOG_SIZE
41#define logSize(s)	printf s
42#else
43#define logSize(s)
44#endif
45
46static void usage(char **argv)
47{
48	printf("usage: %s [options]\n", argv[0]);
49	printf("   Options:\n");
50	printf("   a=algorithm (5=MD5; s=SHA1; 4=SHA224; 2=SHA256; 3=SHA384; 1=SHA512; default=all)\n");
51	printf("   l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
52	printf("   k=keySizeInBytes\n");
53	printf("   m=maxPtextSize (default=%d)\n", MAX_DATA_SIZE);
54	printf("   n=minPtextSize (default=%d)\n", MIN_DATA_SIZE);
55	printf("   p=pauseInterval (default=0, no pause)\n");
56	printf("   s (all ops single-shot, not staged)\n");
57	printf("   S (all ops staged)\n");
58	printf("   z (keys and plaintext all zeroes)\n");
59	printf("   v(erbose)\n");
60	printf("   q(uiet)\n");
61	printf("   h(elp)\n");
62	exit(1);
63}
64
65/*
66 * Given an initialized CCHmacContext, feed it some data and get the result.
67 */
68static void hmacRun(
69	CCHmacContext *ctx,
70	bool randomUpdates,
71	const unsigned char *ptext,
72	size_t ptextLen,
73	void *dataOut)
74{
75	while(ptextLen) {
76		size_t thisMoveIn;			/* input to CCryptUpdate() */
77
78		if(randomUpdates) {
79			thisMoveIn = genRand(1, ptextLen);
80		}
81		else {
82			thisMoveIn = ptextLen;
83		}
84		logSize(("###ptext segment (1) len %lu\n", (unsigned long)thisMoveIn));
85		CCHmacUpdate(ctx, ptext, thisMoveIn);
86		ptext	 += thisMoveIn;
87		ptextLen -= thisMoveIn;
88	}
89	CCHmacFinal(ctx, dataOut);
90}
91
92
93#define MAX_HMAC_SIZE	CC_SHA512_DIGEST_LENGTH
94
95static int doTest(const uint8_t *ptext,
96	size_t ptextLen,
97	CCHmacAlgorithm hmacAlg,
98	uint32 keySizeInBytes,
99	bool stagedOrig,
100	bool stagedClone,
101	bool quiet,
102	bool verbose)
103{
104	uint8_t			keyBytes[MAX_KEY_SIZE];
105	uint8_t			hmacOrig[MAX_HMAC_SIZE];
106	uint8_t			hmacClone[MAX_HMAC_SIZE];
107	int				rtn = 0;
108	CCHmacContext	ctxOrig;
109	CCHmacContext	ctxClone;
110	unsigned		die;		/* 0..3 indicates when to clone */
111	unsigned		loopNum = 0;
112	size_t			hmacLen;
113	bool			didClone = false;
114
115	switch(hmacAlg) {
116		case kCCHmacAlgSHA1:
117			hmacLen = CC_SHA1_DIGEST_LENGTH;
118			break;
119		case kCCHmacAlgMD5:
120			hmacLen = CC_MD5_DIGEST_LENGTH;
121			break;
122		case kCCHmacAlgSHA224:
123			hmacLen = CC_SHA224_DIGEST_LENGTH;
124			break;
125		case kCCHmacAlgSHA256:
126			hmacLen = CC_SHA256_DIGEST_LENGTH;
127			break;
128		case kCCHmacAlgSHA384:
129			hmacLen = CC_SHA384_DIGEST_LENGTH;
130			break;
131		case kCCHmacAlgSHA512:
132			hmacLen = CC_SHA512_DIGEST_LENGTH;
133			break;
134		default:
135			printf("***BRRRZAP!\n");
136			exit(1);
137	}
138
139	/* random key */
140	appGetRandomBytes(keyBytes, keySizeInBytes);
141
142	/* cook up first context */
143	CCHmacInit(&ctxOrig, hmacAlg, keyBytes, keySizeInBytes);
144
145	/* roll the dice */
146	die = genRand(0, 3);
147
148	/*
149	 * In this loop we do updates to the ctxOrig up until we
150	 * clone it, then we use hmacRun to finish both of them.
151	 */
152	while(ptextLen) {
153		if((die == loopNum) || !stagedOrig) {
154			/* make the clone now */
155			if(verbose) {
156				printf("   ...cloning at loop %u\n", loopNum);
157			}
158			ctxClone = ctxOrig;
159			didClone = true;
160
161			/* do all of the clone's updates and final here */
162			hmacRun(&ctxClone, stagedClone, ptext, ptextLen, hmacClone);
163
164			/* now do all remaining updates and final for original */
165			hmacRun(&ctxOrig, stagedOrig, ptext, ptextLen, hmacOrig);
166
167			/* we're all done, time to check the HMAC values */
168			break;
169		}	/* making clone */
170
171		/* feed some data into cryptorOrig */
172		size_t thisMove;
173		if(stagedOrig) {
174			thisMove = genRand(1, ptextLen);
175		}
176		else {
177			thisMove = ptextLen;
178		}
179		logSize(("###ptext segment (2) len %lu\n", (unsigned long)thisMove));
180		CCHmacUpdate(&ctxOrig, ptext, thisMove);
181		ptext += thisMove;
182		ptextLen -= thisMove;
183		loopNum++;
184	}
185
186	/*
187	 * It's possible to get here without cloning or doing any finals,
188	 * if we ran thru multiple updates and finished ptextLen for cryptorOrig
189	 * before we hit the cloning spot.
190	 */
191	if(!didClone) {
192		if(!quiet) {
193			printf("...ctxOrig finished before we cloned; skipping test\n");
194		}
195		return 0;
196	}
197	if(memcmp(hmacOrig, hmacClone, hmacLen)) {
198		printf("***data miscompare\n");
199		rtn = testError(quiet);
200	}
201	return rtn;
202}
203
204bool isBitSet(unsigned bit, unsigned word)
205{
206	if(bit > 31) {
207		printf("We don't have that many bits\n");
208		exit(1);
209	}
210	unsigned mask = 1 << bit;
211	return (word & mask) ? true : false;
212}
213
214int main(int argc, char **argv)
215{
216	int					arg;
217	char				*argp;
218	unsigned			loop;
219	uint8				*ptext;
220	size_t				ptextLen;
221	bool				stagedOrig;
222	bool				stagedClone;
223	const char			*algStr;
224	CCHmacAlgorithm		hmacAlg;
225	int					i;
226	int					currAlg;		// ALG_xxx
227	uint32				keySizeInBytes;
228	int					rtn = 0;
229
230	/*
231	 * User-spec'd params
232	 */
233	bool		keySizeSpec = false;		// false: use rand key size
234	HmacAlg		minAlg = ALG_FIRST;
235	HmacAlg		maxAlg = ALG_LAST;
236	unsigned	loops = LOOPS_DEF;
237	bool		verbose = false;
238	size_t		minPtextSize = MIN_DATA_SIZE;
239	size_t		maxPtextSize = MAX_DATA_SIZE;
240	bool		quiet = false;
241	unsigned	pauseInterval = 0;
242	bool		stagedSpec = false;		// true means caller fixed stagedOrig and stagedClone
243
244	for(arg=1; arg<argc; arg++) {
245		argp = argv[arg];
246		switch(argp[0]) {
247			case 'a':
248				if(argp[1] != '=') {
249					usage(argv);
250				}
251				switch(argp[2]) {
252					case '5':
253						minAlg = maxAlg = ALG_MD5;
254						break;
255					case 's':
256						minAlg = maxAlg = ALG_SHA1;
257						break;
258					case '4':
259						minAlg = maxAlg = ALG_SHA224;
260						break;
261					case '2':
262						minAlg = maxAlg = ALG_SHA256;
263						break;
264					case '3':
265						minAlg = maxAlg = ALG_SHA384;
266						break;
267					case '1':
268						minAlg = maxAlg = ALG_SHA512;
269						break;
270					default:
271						usage(argv);
272				}
273				break;
274		    case 'l':
275				loops = atoi(&argp[2]);
276				break;
277		    case 'n':
278				minPtextSize = atoi(&argp[2]);
279				break;
280		    case 'm':
281				maxPtextSize = atoi(&argp[2]);
282				break;
283		    case 'k':
284		    	keySizeInBytes = atoi(&argp[2]);
285		    	keySizeSpec = true;
286				break;
287		    case 'v':
288		    	verbose = true;
289				break;
290		    case 'q':
291		    	quiet = true;
292				break;
293		    case 'p':
294		    	pauseInterval = atoi(&argp[2]);;
295				break;
296		    case 's':
297		    	stagedOrig = stagedClone = false;
298				stagedSpec = true;
299				break;
300		    case 'S':
301		    	stagedOrig = stagedClone = true;
302				stagedSpec = true;
303				break;
304		    case 'h':
305		    default:
306				usage(argv);
307		}
308	}
309	ptext = (uint8 *)malloc(maxPtextSize);
310	if(ptext == NULL) {
311		printf("Insufficient heap space\n");
312		exit(1);
313	}
314	/* ptext length set in test loop */
315
316	printf("Starting ccHmacClone; args: ");
317	for(i=1; i<argc; i++) {
318		printf("%s ", argv[i]);
319	}
320	printf("\n");
321
322	if(pauseInterval) {
323		fpurge(stdin);
324		printf("Top of test; hit CR to proceed: ");
325		getchar();
326	}
327
328	for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
329		/* when zero, set size randomly or per user setting */
330		switch(currAlg) {
331			case ALG_MD5:
332				hmacAlg = kCCHmacAlgMD5;
333				algStr = "HMACMD5";
334				break;
335			case ALG_SHA1:
336				hmacAlg = kCCHmacAlgSHA1;
337				algStr = "HMACSHA1";
338				break;
339			case ALG_SHA224:
340				hmacAlg = kCCHmacAlgSHA224;
341				algStr = "HMACSHA224";
342				break;
343			case ALG_SHA256:
344				hmacAlg = kCCHmacAlgSHA256;
345				algStr = "HMACSHA256";
346				break;
347			case ALG_SHA384:
348				hmacAlg = kCCHmacAlgSHA384;
349				algStr = "HMACSHA384";
350				break;
351			case ALG_SHA512:
352				hmacAlg = kCCHmacAlgSHA512;
353				algStr = "HMACSHA512";
354				break;
355			default:
356				printf("***BRRZAP!\n");
357				exit(1);
358		}
359		if(!quiet || verbose) {
360			printf("Testing alg %s\n", algStr);
361		}
362		for(loop=1; ; loop++) {
363			ptextLen = genRand(minPtextSize, maxPtextSize);
364			appGetRandomBytes(ptext, ptextLen);
365			if(!keySizeSpec) {
366				keySizeInBytes = genRand(MIN_KEY_SIZE, MAX_KEY_SIZE);
367			}
368
369			/* per-loop settings */
370			if(!stagedSpec) {
371				stagedOrig = isBitSet(1, loop);
372				stagedClone = isBitSet(2, loop);
373			}
374
375			if(!quiet) {
376			   	if(verbose || ((loop % LOOP_NOTIFY) == 0)) {
377					printf("..loop %d ptextLen %4lu keySize %3lu stagedOrig=%d "
378						"stagedClone=%d\n",
379						loop, (unsigned long)ptextLen, (unsigned long)keySizeInBytes,
380						(int)stagedOrig, (int)stagedClone);
381				}
382			}
383
384			if(doTest(ptext, ptextLen,
385					hmacAlg, keySizeInBytes,
386					stagedOrig, stagedClone, quiet, verbose)) {
387				rtn = 1;
388				break;
389			}
390			if(pauseInterval && ((loop % pauseInterval) == 0)) {
391				char c;
392				fpurge(stdin);
393				printf("Hit CR to proceed, q to abort: ");
394				c = getchar();
395				if(c == 'q') {
396					goto testDone;
397				}
398			}
399			if(loops && (loop == loops)) {
400				break;
401			}
402		}	/* main loop */
403		if(rtn) {
404			break;
405		}
406
407	}	/* for algs */
408
409testDone:
410	if(pauseInterval) {
411		fpurge(stdin);
412		printf("ModuleDetach/Unload complete; hit CR to exit: ");
413		getchar();
414	}
415	if((rtn == 0) && !quiet) {
416		printf("%s test complete\n", argv[0]);
417	}
418	free(ptext);
419	return rtn;
420}
421
422
423