symReference.cpp   [plain text]


/* 
 * symReference.c - write keys and ciphertext blobs, read them back
 *                  and decrypt on (possibly) a different platfrom.
 *					Intended for use in testing multiplatform
 *					compatibility (e.g. encrypt on 32 bit G4, decrypt
 *					on 64-bit G5). 
 *
 * Created by Doug Mitchell 10/31/05. 
 */

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <Security/cssm.h>
#include <Security/cssmapple.h>
#include "cspwrap.h"
#include <security_cdsa_utils/cuFileIo.h>
#include "common.h"
#include <string.h>
#include "cspdlTesting.h"
#include <unistd.h>

/*
 * Defaults.
 */
#define LOOPS_DEF		200
#define PTEXT_SIZE_DEF	256
#define BLOCK_SIZE_MAX	32		/* bytes */

/*
 * Enumerate algs our own way to allow iteration.
 */
typedef enum {
	ALG_ASC = 0,		/* first must be 0 */
	ALG_DES,
	ALG_RC2,
	ALG_RC4,
	ALG_RC5,
	ALG_3DES,
	ALG_AES,
	ALG_AES192,
	ALG_AES256,
	ALG_BFISH,
	ALG_CAST
} SymAlg;

#define ALG_FIRST			ALG_ASC
#define ALG_LAST			ALG_CAST

static void usage(char **argv)
{
	printf("usage: %s e|d dirName [options]\n", argv[0]);
	printf("  e=encrypt, d=decrypt; blobs read/written in dirName\n");
	printf("   Options:\n");
	printf("   a=algorithm (d=DES; 3=3DES3; 2=RC2; 4=RC4; 5=RC5; a=AES; b=Blowfish; \n");
	printf("                c=CAST; s=ASC, default=all)\n");
	printf("   p=ptextSize (default=%d)\n", PTEXT_SIZE_DEF);
	printf("   D (CSP/DL; default = bare CSP)\n");
	printf("   v(erbose)\n");
	printf("   q(uiet)\n");
	printf("   h(elp)\n");
	exit(1);
}

/*
 * map SymAlg to test params
 */
typedef struct {
	SymAlg				alg;
	const char			*algStr;
	CSSM_ALGORITHMS		cssmAlg;
	CSSM_ENCRYPT_MODE	mode;
	CSSM_PADDING		padding;
	CSSM_SIZE			keySizeBits;
	CSSM_SIZE			ivLen;		// in bytes
} SymAlgParams;

static const SymAlgParams symAlgParams[] = 
{
	{ ALG_ASC, "ASC", CSSM_ALGID_ASC, CSSM_ALGMODE_NONE, CSSM_PADDING_NONE, 
		CSP_ASC_KEY_SIZE_DEFAULT, 0 },
	{ ALG_DES, "DES", CSSM_ALGID_DES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5, 
		CSP_DES_KEY_SIZE_DEFAULT, 8 },
	{ ALG_RC2, "RC2", CSSM_ALGID_RC2, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5, 
		CSP_RC2_KEY_SIZE_DEFAULT, 8 },
	{ ALG_RC4, "RC4", CSSM_ALGID_RC4, CSSM_ALGMODE_NONE, CSSM_PADDING_NONE, 
		CSP_RC4_KEY_SIZE_DEFAULT, 0 },
	{ ALG_RC5, "RC5", CSSM_ALGID_RC5, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5, 
		CSP_RC5_KEY_SIZE_DEFAULT, 8 },
	{ ALG_3DES, "3DES", CSSM_ALGID_3DES_3KEY_EDE, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5, 
		CSP_DES3_KEY_SIZE_DEFAULT, 8 },
	{ ALG_AES, "AES", CSSM_ALGID_AES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5, 
		CSP_AES_KEY_SIZE_DEFAULT, 16 },
	{ ALG_AES192, "AES192", CSSM_ALGID_AES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5, 
		192, 24 },
	{ ALG_AES256, "AES256", CSSM_ALGID_AES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5, 
		256, 32 },
	{ ALG_BFISH, "Blowfish", CSSM_ALGID_BLOWFISH, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
		CSP_BFISH_KEY_SIZE_DEFAULT,  8 },
	{ ALG_CAST, "CAST", CSSM_ALGID_CAST, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5, 
		CSP_CAST_KEY_SIZE_DEFAULT,  8 }
};

static void genFileNames(
	const char	*algStr,
	char		*keyFile,
	char		*ptextFile,
	char		*ctextFile,
	char		*ivFile)
{
	sprintf(keyFile,	"key_%s", algStr);
	sprintf(ptextFile,	"ptext_%s", algStr);
	sprintf(ctextFile,	"ctext_%s", algStr);
	sprintf(ivFile,		"iv_%s", algStr);
}
	
/* encrypt, write blobs (key, plaintext, ciphertext, optional IV) to disk */
static int doEncrypt(
	CSSM_CSP_HANDLE		cspHand,
	const SymAlgParams	*algParams,
	CSSM_DATA			*ptext,		// mallocd, length valid, we fill data
	CSSM_BOOL			quiet,
	CSSM_BOOL			verbose)
{
	CSSM_KEY_PTR	symKey = NULL;
	CSSM_KEY		rawKey;
	CSSM_RETURN		crtn;
	CSSM_DATA		ctext = {0, NULL};
	uint8			iv[BLOCK_SIZE_MAX];
	CSSM_DATA		ivd = {BLOCK_SIZE_MAX, iv};
	CSSM_DATA		*ivp = NULL;
	uint32			blockSize = 0;
	char			keyFile[FILENAME_MAX];
	char			ptextFile[FILENAME_MAX];
	char			ctextFile[FILENAME_MAX];
	char			ivFile[FILENAME_MAX];
	
	if(!quiet) {
		printf("...encrypting, alg %s\n", algParams->algStr);
	}

	/* generate reference key (works with CSPDL) */
	symKey = cspGenSymKey(cspHand, algParams->cssmAlg,
		"noLabel", 7,
		CSSM_KEYUSE_ANY, algParams->keySizeBits, CSSM_TRUE);
	if(symKey == NULL) {
		printf("***Error generating key for alg %s size %u bits\n",
			algParams->algStr, (unsigned)algParams->keySizeBits);
		return testError(quiet);
	}
	
	/* get key in raw format (to get the raw blob we write to disk) */
	crtn = cspRefKeyToRaw(cspHand, symKey, &rawKey);
	if(crtn) {
		printf("***Error generating raw key for alg %s size %u bits\n",
			algParams->algStr, (unsigned)algParams->keySizeBits);
		return testError(quiet);
	}
	
	appGetRandomBytes(ptext->Data, (unsigned)ptext->Length);
	
	/* 
	 * Hack: we only need to specify block size for AES192 and AES256, which 
	 * we detect by their having an ivLen of greater than 16.
	 */
	if(algParams->ivLen > 16) {
		blockSize = algParams->ivLen;
	}
	if(algParams->ivLen) {
		appGetRandomBytes(iv, algParams->ivLen);
		ivd.Length = algParams->ivLen;
		ivp = &ivd;
	}
	
	crtn = cspStagedEncrypt(cspHand,
		algParams->cssmAlg, algParams->mode, algParams->padding,
		symKey, NULL,
		0, blockSize, 0,
		ivp, ptext,
		&ctext, 
		CSSM_FALSE);
	if(crtn) {
		printf("***Error encrypting for alg %s size %u bits\n",
			algParams->algStr, (unsigned)algParams->keySizeBits);
		return testError(quiet);
	}

	/* write: key, IV, ptext, ctext */
	genFileNames(algParams->algStr, keyFile, ptextFile, ctextFile, ivFile);
	if(writeFile(keyFile, rawKey.KeyData.Data, (unsigned)rawKey.KeyData.Length) ||
	   writeFile(ptextFile, ptext->Data, (unsigned)ptext->Length) ||
	   writeFile(ctextFile, ctext.Data, (unsigned)ctext.Length)) {
		printf("***Error writing result of alg %s size %u bits\n",
			algParams->algStr, (unsigned)algParams->keySizeBits);
		return testError(quiet);
	}
	if(ivp != NULL) {
		if(writeFile(ivFile, ivp->Data, (unsigned)ivp->Length)) {
			printf("***Error writing IV for alg %s size %u bits\n",
				algParams->algStr, (unsigned)algParams->keySizeBits);
			return testError(quiet);
		}
	}
	
	/* Free resources */
	CSSM_FreeKey(cspHand, NULL, symKey, CSSM_FALSE);
	CSSM_FreeKey(cspHand, NULL, &rawKey, CSSM_FALSE);
	CSSM_FREE(ctext.Data);
	return 0;
}

/* read blobs (key, plaintext, ciphertext, optional IV) from disk, decrypt, compare plaintext */
static int doDecrypt(
	CSSM_CSP_HANDLE		cspHand,
	const SymAlgParams	*algParams,
	CSSM_BOOL			quiet,
	CSSM_BOOL			verbose)
{
	CSSM_KEY		symKey;
	uint8			*symKeyBits;
	unsigned		symKeyLen;				// in bytes
	CSSM_DATA		symKeyData;
	CSSM_RETURN		crtn;
	uint8			*ctextChars;
	unsigned		ctextLen = 0;
	CSSM_DATA		ctext;
	CSSM_DATA		rptext = {0, NULL};		// recovered/decrytped
	uint8			*refPTextChars;
	unsigned		refPtextLen;
	CSSM_DATA		refPtext = {0, NULL};	// expected
	uint8			*iv = NULL;
	unsigned		ivLen;
	CSSM_DATA		ivd = {BLOCK_SIZE_MAX, iv};
	CSSM_DATA		*ivp = NULL;
	uint32			blockSize = 0;
	char			keyFile[FILENAME_MAX];
	char			ptextFile[FILENAME_MAX];
	char			ctextFile[FILENAME_MAX];
	char			ivFile[FILENAME_MAX];
	
	if(!quiet) {
		printf("...decrypting, alg %s\n", algParams->algStr);
	}
	
	/* 
	 * Hack: we only need to specify block size for AES192 and AES256, which 
	 * we detect by their having an ivLen of greater than 16.
	 */
	if(algParams->ivLen > 16) {
		blockSize = algParams->ivLen;
	}
	if(algParams->ivLen) {
		ivp = &ivd;
		ivd.Length = algParams->ivLen;
	}

	/* read: key, IV, ptext, ctext */
	genFileNames(algParams->algStr, keyFile, ptextFile, ctextFile, ivFile);
	if(readFile(keyFile, &symKeyBits, &symKeyLen) ||
	   readFile(ptextFile, &refPTextChars, &refPtextLen) ||
	   readFile(ctextFile, &ctextChars, &ctextLen)) {
		printf("***Error reading reference blobs for alg %s size %u bits\n",
			algParams->algStr, (unsigned)algParams->keySizeBits);
		return testError(quiet);
	}
	if(ivp != NULL) {
		if(readFile(ivFile, &iv, &ivLen)) {
			printf("***Error writing IV for alg %s size %u bits\n",
				algParams->algStr, (unsigned)algParams->keySizeBits);
			return testError(quiet);
		}
		if(ivLen != algParams->ivLen) {
			printf("***Unexpected IV length: expect %u found %u\n",
				(unsigned)algParams->ivLen, (unsigned)ivLen);
			if(testError(quiet)) {
				return 1;
			}
		}
		ivd.Data = iv;
	}
	ctext.Data = ctextChars;
	ctext.Length = ctextLen;
	refPtext.Data = refPTextChars;
	refPtext.Length = refPtextLen;
	
	/* generate key */
	symKeyData.Data = symKeyBits;
	symKeyData.Length = symKeyLen;
	
	crtn = cspGenSymKeyWithBits(cspHand, algParams->cssmAlg,
		CSSM_KEYUSE_ANY, &symKeyData, symKeyLen, &symKey);
	if(crtn) {
		printf("***Error creating key for alg %s keySize %u\n",
			algParams->algStr, (unsigned)algParams->keySizeBits);
		return testError(quiet);
	}
	
	crtn = cspStagedDecrypt(cspHand,
		algParams->cssmAlg, algParams->mode, algParams->padding,
		&symKey, NULL,
		0, blockSize, 0,
		ivp, &ctext,
		&rptext, 
		CSSM_FALSE);
	if(crtn) {
		printf("***Error decrypting for alg %s size %u bits\n",
			algParams->algStr, (unsigned)algParams->keySizeBits);
		return testError(quiet);
	}

	/* moment of truth */
	if(!appCompareCssmData(&rptext, &refPtext)) {
		printf("***DATA MISCOMPARE AFTER DECRYPT alg %s size %u bits\n",
			algParams->algStr, (unsigned)algParams->keySizeBits);
		return testError(quiet);
	}
	
	/* Free resources */
	CSSM_FreeKey(cspHand, NULL, &symKey, CSSM_FALSE);
	free(symKeyBits);		// mallocd by readFile()
	free(refPTextChars);
	free(ctextChars);
	CSSM_FREE(rptext.Data);	// mallocd by CSP
	if(iv) {
		free(iv);
	}
	return 0;
}


int main(int argc, char **argv)
{
	int					arg;
	char				*argp;
	CSSM_DATA			ptext;
	CSSM_CSP_HANDLE 	cspHand;
	unsigned			currAlg;				// ALG_xxx
	int					rtn = 0;

	/*
	 * User-spec'd params
	 */
	unsigned	minAlg = ALG_FIRST;
	unsigned	maxAlg = ALG_LAST;
	CSSM_BOOL	verbose = CSSM_FALSE;
	CSSM_BOOL	quiet = CSSM_FALSE;
	CSSM_BOOL	bareCsp = CSSM_TRUE;
	bool		encrypt = false;
	unsigned	ptextSize = PTEXT_SIZE_DEF;
	char		*dirName;
		
	if(argc < 3) {
		usage(argv);
	}
	switch(argv[1][0]) {
		case 'e':
			encrypt = true;
			break;
		case 'd':
			encrypt = false;
			break;
		default:
			usage(argv);
	}
	dirName = argv[2];
	
	for(arg=3; arg<argc; arg++) {
		argp = argv[arg];
		switch(argp[0]) {
			case 'a':
				if(argp[1] != '=') {
					usage(argv);
				}
				switch(argp[2]) {
					case 's':
						minAlg = maxAlg = ALG_ASC;
						break;
					case 'd':
						minAlg = maxAlg = ALG_DES;
						break;
					case '3':
						minAlg = maxAlg = ALG_3DES;
						break;
					case '2':
						minAlg = maxAlg = ALG_RC2;
						break;
					case '4':
						minAlg = maxAlg = ALG_RC4;
						break;
					case '5':
						minAlg = maxAlg = ALG_RC5;
						break;
					case 'a':
						minAlg = maxAlg = ALG_AES;
						break;
					case 'b':
						minAlg = maxAlg = ALG_BFISH;
						break;
					case 'c':
						minAlg = maxAlg = ALG_CAST;
						break;
					default:
						usage(argv);
				}
				break;
		    case 'v':
		    	verbose = CSSM_TRUE;
				break;
			case 'D':
				bareCsp = CSSM_FALSE;
				break;
			case 'p':
				ptextSize = atoi(&argp[2]);
				break;
		    case 'q':
		    	quiet = CSSM_TRUE;
				break;
		    case 'h':
		    default:
				usage(argv);
		}
	}
	ptext.Data = (uint8 *)CSSM_MALLOC(ptextSize);
	if(ptext.Data == NULL) {
		printf("Insufficient heap space\n");
		exit(1);
	}
	ptext.Length = ptextSize;

	testStartBanner("symReference", argc, argv);

	cspHand = cspDlDbStartup(bareCsp, NULL);
	if(cspHand == 0) {
		exit(1);
	}

	if(chdir(dirName)) {
		perror(dirName);
		printf("Error accessing directory %s. Aborting.\n", dirName);
		exit(1);
	}
	for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
		const SymAlgParams *algParams = &symAlgParams[currAlg];

		if(encrypt) {
			rtn = doEncrypt(cspHand, algParams, &ptext, quiet, verbose);
		}
		else {
			rtn = doDecrypt(cspHand, algParams, quiet, verbose);
		}
		if(rtn) {
			break;
		}
	}	/* for algs */
	
	cspShutdown(cspHand, bareCsp);
	if((rtn == 0) && !quiet) {
		printf("%s test complete\n", argv[0]);
	}
	CSSM_FREE(ptext.Data);
	return rtn;
}