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