badmac.c   [plain text]


/* Copyright (c) 1997,2003-2005,2008 Apple Inc.
 *
 * badmac.c - Verify bad MAC detect.
 *
 * Revision History
 * ----------------
 *   4 May 2000  Doug Mitchell
 *		Ported to X/CDSA2. 
 *  21 Dec 1998	Doug Mitchell at Apple
 *		Created.
 */
/*
 * text size =       {random, from 100 bytes to 1 megabyte, in
 *                   geometrical steps, i.e. the number of
 *                   bytes would be 10^r, where r is random out of
 *                   {2,3,4,5,6}, plus a random integer in {0,..99}};
 *
 * for loop_count
 *     text contents = {random data, random size as specified above};
 *     generate random MAC key;
 *     generate MAC, validate;
 *     for various bytes of ptext {
 *        corrupt text byte;
 *        verify bad MAC;
 *        restore corrupted byte;
 *     }
 *  }
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <Security/cssm.h>
#include "cspwrap.h"
#include "common.h"
#include "cspdlTesting.h"

#define USAGE_NAME		"noUsage"
#define USAGE_NAME_LEN	(strlen(USAGE_NAME))

/*
 * HMAC/SHA1 can not do multiple updates with BSAFE (though the CSP's current 
 * internal implementation can.)
 * Fixed in Puma; the bug was in BSAFE. 
 */
#define HMACSHA_MULTI_UPDATES	1

/*
 * Defaults.
 */
#define LOOPS_DEF			10
#define MIN_EXP				2		/* for data size 10**exp */
#define DEFAULT_MAX_EXP		2
#define MAX_EXP				5
#define INCR_DEFAULT		0		/* munge every incr bytes - zero means
									 * "adjust per ptext size" */

/*
 * Enumerate algs our own way to allow iteration.
 */
#define ALG_SHA1HMAC		1
#define ALG_MD5HMAC			2
#define ALG_FIRST			ALG_SHA1HMAC
#define ALG_LAST			ALG_MD5HMAC
#define MAX_DATA_SIZE		(100000 + 100)	/* bytes */

static void usage(char **argv)
{
	printf("usage: %s [options]\n", argv[0]);
	printf("   Options:\n");
	printf("   l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
	printf("   n=minExp (default=%d)\n", MIN_EXP);
	printf("   x=maxExp (default=%d, max=%d)\n", DEFAULT_MAX_EXP, MAX_EXP);
	printf("   i=increment (default=%d)\n", INCR_DEFAULT);
	printf("   r(eference keys only)\n");
	printf("   m (CSM mallocs MAC)\n");
	printf("   p=pauseInterval (default=0, no pause)\n");
	printf("   D (CSP/DL; default = bare CSP)\n");
	printf("   v(erbose)\n");
	printf("   q(uiet)\n");
	printf("   h(elp)\n");
	exit(1);
}

#define LOG_FREQ	20
static int doTest(CSSM_CSP_HANDLE cspHand,
	uint32 macAlg,					// CSSM_ALGID_xxx mac algorithm
	CSSM_DATA_PTR ptext,
	CSSM_BOOL verbose,
	CSSM_BOOL quiet,
	unsigned keySize,
	unsigned incr,
	CSSM_BOOL stagedGen,
	CSSM_BOOL stagedVerify,
	CSSM_BOOL mallocMac,
	CSSM_BOOL refKey)
{
	CSSM_KEY_PTR	symmKey;
	CSSM_DATA 		mac = {0, NULL};
	unsigned		length;
	unsigned		byte;
	unsigned char	*data;
	unsigned char	origData;
	unsigned char	bits;
	int				rtn = 0;
	CSSM_RETURN		crtn;
	uint32			keyGenAlg;
	unsigned		loop = 0;
	
	switch(macAlg) {
		case CSSM_ALGID_SHA1HMAC:
			keyGenAlg = CSSM_ALGID_SHA1HMAC;
			break;
		case CSSM_ALGID_MD5HMAC:
			keyGenAlg = CSSM_ALGID_MD5HMAC;
			break;
		default:
			printf("bogus algorithm\n");
			return 1;
	}
	symmKey = cspGenSymKey(cspHand,
		keyGenAlg,
		"noLabel",
		7,
		CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY,
		keySize,
		refKey);
	if(symmKey == NULL) {
		rtn = testError(quiet);
		goto abort;
	}
	if(stagedGen) {
		crtn = cspStagedGenMac(cspHand,
			macAlg,
			symmKey,
			ptext,
			mallocMac,
			CSSM_TRUE,			// multi
			&mac);
	}
	else {
		crtn = cspGenMac(cspHand,
			macAlg,
			symmKey,
			ptext,
			&mac);
	}
	if(crtn) {
		rtn = 1;
		goto abort;
	}
	if(stagedVerify) {
		crtn = cspStagedMacVerify(cspHand,
			macAlg,
			symmKey,
			ptext,
			&mac,
			CSSM_TRUE,			// multi
			CSSM_OK);			// expectedResult
	}
	else {
		crtn = cspMacVerify(cspHand,
			macAlg,
			symmKey,
			ptext,
			&mac,
			CSSM_OK);
	}
	if(crtn) {
		printf("**Unexpected BAD MAC\n");
		return testError(quiet);
	}
	data = (unsigned char *)ptext->Data;
	length = ptext->Length;
	for(byte=0; byte<length; byte += incr) {
		if(verbose && ((loop++ % LOG_FREQ) == 0)) {
			printf("  ..byte %d\n", byte);
		}
		origData = data[byte];
		/*
		 * Generate random non-zero byte
		 */
		do {
			bits = genRand(1, 0xff) & 0xff;
		} while(bits == 0);
		data[byte] ^= bits;
		if(stagedVerify) {
			crtn = cspStagedMacVerify(cspHand,
				macAlg,
				symmKey,
				ptext,
				&mac,
				CSSM_TRUE,					// multi
				CSSMERR_CSP_VERIFY_FAILED);	// expect failure
		}
		else {
			crtn = cspMacVerify(cspHand,
				macAlg,
				symmKey,
				ptext,
				&mac,
				CSSMERR_CSP_VERIFY_FAILED);
		}
		if(crtn) {
			return testError(quiet);
		}
		data[byte] = origData;
	}
abort:
	/* free key */
	if(cspFreeKey(cspHand, symmKey)) {
		printf("Error freeing symmKey\n");
		rtn = 1;
	}
	CSSM_FREE(mac.Data);
	return rtn;
}

int main(int argc, char **argv)
{
	int					arg;
	char				*argp;
	unsigned			loop;
	CSSM_DATA			ptext;
	CSSM_CSP_HANDLE 	CSPHandle;
	CSSM_BOOL			stagedSign;
	CSSM_BOOL 			stagedVfy;
	CSSM_BOOL 			mallocMac;
	CSSM_BOOL 			refKey;
	const char 			*algStr;
	unsigned			actualIncr;
	uint32				macAlg;			// CSSM_ALGID_xxx
	unsigned			currAlg;		// ALG_xxx
	int					i;
	int 				rtn = 0;
	
	/*
	 * User-spec'd params
	 */
	unsigned	loops = LOOPS_DEF;
	CSSM_BOOL	verbose = CSSM_FALSE;
	unsigned	minExp = MIN_EXP;
	unsigned	maxExp = DEFAULT_MAX_EXP;
	CSSM_BOOL	quiet = CSSM_FALSE;
	unsigned	keySizeInBits = CSP_KEY_SIZE_DEFAULT;
	unsigned	incr = INCR_DEFAULT;
	unsigned	minAlg = ALG_FIRST;
	uint32		maxAlg = ALG_LAST;
	unsigned	pauseInterval = 0;
	CSSM_BOOL	bareCsp = CSSM_TRUE;
	CSSM_BOOL	refKeysOnly = CSSM_FALSE;
	CSSM_BOOL	cspMallocs = CSSM_FALSE;
	
	#if	macintosh
	argc = ccommand(&argv);
	#endif
	for(arg=1; arg<argc; arg++) {
		argp = argv[arg];
		switch(argp[0]) {
			/* no Alg or keySizeInBits spec for now */
		    case 'l':
				loops = atoi(&argp[2]);
				break;
		    case 'n':
				minExp = atoi(&argp[2]);
				break;
		    case 'x':
				maxExp = atoi(&argp[2]);
				if(maxExp > MAX_EXP) {
					usage(argv);
				}
				break;
		    case 'i':
				incr = atoi(&argp[2]);
				break;
		    case 'p':
				pauseInterval = atoi(&argp[2]);
				break;
			case 'D':
				bareCsp = CSSM_FALSE;
				#if CSPDL_ALL_KEYS_ARE_REF
				refKeysOnly = CSSM_TRUE;
				#endif
				break;
			case 'r':
				refKeysOnly = CSSM_TRUE;
				break;
			case 'm':
				cspMallocs = CSSM_TRUE;
				break;
		    case 'v':
		    	verbose = CSSM_TRUE;
				break;
		    case 'q':
		    	quiet = CSSM_TRUE;
				break;
		    case 'h':
		    default:
				usage(argv);
		}
	}
	ptext.Data = (uint8 *)CSSM_MALLOC(MAX_DATA_SIZE);
	/* length set in test loop */
	if(ptext.Data == NULL) {
		printf("Insufficient heap\n");
		exit(1);
	}
	printf("Starting badmac; args: ");
	for(i=1; i<argc; i++) {
		printf("%s ", argv[i]);
	}
	printf("\n");
	CSPHandle = cspDlDbStartup(bareCsp, NULL);
	if(CSPHandle == 0) {
		exit(1);
	}
	for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
		switch(currAlg) {
			case ALG_SHA1HMAC:
				macAlg = CSSM_ALGID_SHA1HMAC;
				algStr = "SHA1HMAC";
				break;
			case ALG_MD5HMAC:
				macAlg = CSSM_ALGID_MD5HMAC;
				algStr = "MD5HMAC";
				break;
		}
		if(!quiet) {
			printf("Testing alg %s\n", algStr);
		}
		for(loop=1; ; loop++) {
			ptext.Length = genData(ptext.Data, minExp, maxExp, DT_Random);
			if(!quiet) {
				printf("..loop %d text size %lu\n", loop, ptext.Length);
			}
			if(incr == 0) {
				/* adjust increment as appropriate */
				actualIncr = (ptext.Length / 50) + 1;
			}
			else {
				actualIncr = incr;
			}
			/* mix up staging & ref key format*/
			stagedSign = (loop & 1) ? CSSM_TRUE : CSSM_FALSE;
			stagedVfy  = (loop & 2) ? CSSM_TRUE : CSSM_FALSE;
			if(refKeysOnly) {
				refKey = CSSM_TRUE;
			}
			else {
				refKey     = (loop & 4) ? CSSM_TRUE : CSSM_FALSE;
			}
			if(cspMallocs) {
				mallocMac = CSSM_FALSE;
			}
			else {
				mallocMac  = (loop & 8) ? CSSM_TRUE : CSSM_FALSE;
			}

			#if		!HMACSHA_MULTI_UPDATES
			if(macAlg == CSSM_ALGID_SHA1HMAC) {
				stagedSign = stagedVfy = CSSM_FALSE;
			}
			#endif	/* HMACSHA_MULTI_UPDATES */
			
			if(!quiet) {
				printf("  stagedSign %d  stagedVfy %d refKey %d mallocMac %d\n",
					 (int)stagedSign, (int)stagedVfy, (int)refKey, (int)mallocMac);
			}
			if(doTest(CSPHandle,
					macAlg,
					&ptext,
					verbose,
					quiet,
					keySizeInBits,
					actualIncr,
					stagedSign,
					stagedVfy,
					mallocMac,
					refKey)) {
				rtn = 1;
				goto testDone;
			}
			if(loops && (loop == loops)) {
				break;
			}
			if(pauseInterval && ((loop % pauseInterval) == 0)) {
				fpurge(stdin);
				printf("Hit CR to proceed: ");
				getchar();
			}
		}	/* for loop */
	}		/* for alg */
testDone:
	CSSM_ModuleDetach(CSPHandle);
	if((rtn == 0) && !quiet) {
		printf("%s test complete\n", argv[0]);
	}
	return rtn;
}