pbeTest.c   [plain text]


/* Copyright (c) 1998,2003-2005,2008 Apple Inc.
 *
 * pbeTest.c - test CSP PBE-style DeriveKey().
 *
 * Revision History
 * ----------------
 *  15 May 2000 Doug Mitchell
 *		Ported to X/CDSA2. 
 *  13 Aug 1998	Doug Mitchell at NeXT
 *		Created.
 */
#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"

/* we need to know a little bit about AES for this test.... */
#define AES_BLOCK_SIZE		16		/* bytes */

/*
 * Defaults.
 */
#define LOOPS_DEF			10
#define MIN_PTEXT_SIZE		AES_BLOCK_SIZE		/* for non-padding tests */
#define MAX_PTEXT_SIZE		1000
#define MAX_PASSWORD_SIZE	64
#define MAX_SALT_SIZE		32
#define MIN_ITER_COUNT		1000
#define MAX_ITER_COUNT		2000
#define MAX_IV_SIZE			AES_BLOCK_SIZE

/* min values not currently exported by CSP */
#define APPLE_PBE_MIN_PASSWORD	8
#define APPLE_PBE_MIN_SALT		8

/* static IV for derive algorithms which don't create one */
CSSM_DATA staticIv = {MAX_IV_SIZE, (uint8 *)"someIvOrOther..."};

/*
 * Enumerate algs our own way to allow iteration.
 */
typedef unsigned privAlg;
enum {
	/* PBE algs */
	pbe_pbkdf2 = 1,
	// other unsupported for now
	pbe_PKCS12 = 1,
	pbe_MD5,
	pbe_MD2,
	pbe_SHA1,
	
	/* key gen algs */
	pka_ASC,
	pka_RC4,
	pka_DES,
	pka_RC2,
	pka_RC5,
	pka_3DES,
	pka_AES
};

#define PBE_ALG_FIRST			pbe_pbkdf2
#define PBE_ALG_LAST			pbe_pbkdf2
#define ENCR_ALG_FIRST			pka_ASC
#define ENCR_ALG_LAST			pka_AES
#define ENCR_ALG_LAST_EXPORT	pka_RC5

/*
 * Args passed to each test
 */
typedef struct {
	CSSM_CSP_HANDLE 	cspHand;
	CSSM_ALGORITHMS		keyAlg;
	CSSM_ALGORITHMS		encrAlg;
	uint32				keySizeInBits;
	uint32				effectiveKeySizeInBits;	// 0 means not used
	const char 			*keyAlgStr;
	CSSM_ENCRYPT_MODE	encrMode;
	CSSM_PADDING		encrPad;
	CSSM_ALGORITHMS		deriveAlg;
	const char 			*deriveAlgStr;
	CSSM_DATA_PTR		ptext;
	CSSM_DATA_PTR		password;
	CSSM_DATA_PTR 		salt;
	uint32 				iterCount;
	CSSM_BOOL 			useInitVector;		// encrypt needs an IV
	uint32				ivSize;
	CSSM_BOOL			genInitVector;		// DeriveKey generates an IV
	CSSM_BOOL			useRefKey;
	CSSM_BOOL			quiet;
} testArgs;

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("   e(xport)\n");
	printf("   r(epeatOnly)\n");
	printf("   z(ero length password)\n");
	printf("   p(ause after each loop)\n");
	printf("   D (CSP/DL; default = bare CSP)\n");
	printf("   q(uiet)\n");
	printf("   h(elp)\n");
	exit(1);
}

/*
 * Given a privAlg value, return various associated stuff.
 */
static void algInfo(privAlg alg,			// pbe_MD5, etc.
	CSSM_ALGORITHMS		*cdsaAlg,		// CSSM_ALGID_MD5_PBE, etc. RETURNED
										//   key alg for key gen algs
	CSSM_ALGORITHMS		*encrAlg,		// encrypt/decrypt alg for key 
										//   gen algs
	CSSM_ENCRYPT_MODE	*mode,			// RETURNED
	CSSM_PADDING 		*padding,		// RETURNED
	CSSM_BOOL			*useInitVector,	// RETURNED, for encrypt/decrypt
	uint32				*ivSize,		// RETURNED, in bytes
	CSSM_BOOL			*genInitVector,	// RETURNED, for deriveKey
	const char			**algStr)		// RETURNED
{
	/* default or irrelevant fields */
	*mode = CSSM_ALGMODE_NONE;
	*useInitVector = CSSM_FALSE;
	*genInitVector = CSSM_FALSE;		// DeriveKey doesn't do this now
	*padding = CSSM_PADDING_PKCS1;
	*ivSize = 8;						// thje usual size, if needed
	*encrAlg = CSSM_ALGID_NONE;
	
	switch(alg) {
		case pbe_pbkdf2:
			*cdsaAlg = CSSM_ALGID_PKCS5_PBKDF2;
			*algStr = "PBKDF2";
			return;
		/* these are not supported */
		#if 0
		case pbe_PKCS12:
			*cdsaAlg = CSSM_ALGID_SHA1_PBE_PKCS12;
			*algStr = "PKCS12";
			return;
		case pbe_MD5:
			*cdsaAlg = CSSM_ALGID_MD5_PBE;
			*algStr = "MD5";
			return;
		case pbe_MD2:
			*cdsaAlg = CSSM_ALGID_MD2_PBE;
			*algStr = "MD2";
			return;
		case pbe_SHA1:
			*cdsaAlg = CSSM_ALGID_SHA1_PBE;
			*algStr = "SHA1";
			return;
		case pka_ASC:
			*cdsaAlg = CSSM_ALGID_ASC;
			*algStr = "ASC";
			return;
		#endif 
		case pka_DES:
			*cdsaAlg = *encrAlg = CSSM_ALGID_DES;
			*useInitVector = CSSM_TRUE;
			*mode = CSSM_ALGMODE_CBCPadIV8;
			*algStr = "DES";
			return;
		case pka_3DES:
			*cdsaAlg = CSSM_ALGID_3DES_3KEY;
			*encrAlg = CSSM_ALGID_3DES_3KEY_EDE;
			*useInitVector = CSSM_TRUE;
			*mode = CSSM_ALGMODE_CBCPadIV8;
			*algStr = "3DES";
			return;
		case pka_AES:
			*cdsaAlg = *encrAlg = CSSM_ALGID_AES;
			*useInitVector = CSSM_TRUE;
			*mode = CSSM_ALGMODE_CBCPadIV8;
			*padding = CSSM_PADDING_PKCS5;
			*ivSize = AES_BLOCK_SIZE;			// per the default block size
			*algStr = "AES";
			return;
		case pka_RC2:
			*cdsaAlg = *encrAlg = CSSM_ALGID_RC2;
			*useInitVector = CSSM_TRUE;
			*mode = CSSM_ALGMODE_CBCPadIV8;
			*algStr = "RC2";
			return;
		case pka_RC4:
			*cdsaAlg = *encrAlg = CSSM_ALGID_RC4;
			/* initVector false */
			*mode = CSSM_ALGMODE_NONE;
			*algStr = "RC4";
			return;
		case pka_RC5:
			*cdsaAlg = *encrAlg = CSSM_ALGID_RC5;
			*algStr = "RC5";
			*mode = CSSM_ALGMODE_CBCPadIV8;
			*useInitVector = CSSM_TRUE;
			return;
		case pka_ASC:
			*cdsaAlg = *encrAlg = CSSM_ALGID_ASC;
			/* initVector false */
			*algStr = "ASC";
			*mode = CSSM_ALGMODE_NONE;
			return;
		default:
			printf("BRRZZZT! Update algInfo()!\n");
			testError(CSSM_TRUE);
	}
}

/* a handy "compare two CSSM_DATAs" ditty */
static CSSM_BOOL compareData(const CSSM_DATA_PTR d1,
	const CSSM_DATA_PTR d2)
{
	if(d1->Length != d2->Length) {
		return CSSM_FALSE;
	}
	if(memcmp(d1->Data, d2->Data, d1->Length)) {
		return CSSM_FALSE;
	}
	return CSSM_TRUE;
}

/* generate random one-bit byte */
static uint8 randBit()
{
	return 1 << genRand(0, 7);
}

/*
 * Writer debug - assertion failure when ctext[1].Data is NULL
 * but length is nonzero
 */
#define SAFE_CTEXT_ARRAY	0

/*
 * Encrypt ptext using specified key, IV, effectiveKeySizeInBits
 */
static int encryptCom(CSSM_CSP_HANDLE cspHand,
	const char *testName,
	CSSM_DATA_PTR ptext,
	CSSM_KEY_PTR key,
	CSSM_ALGORITHMS alg,
	CSSM_ENCRYPT_MODE mode,
	CSSM_PADDING padding,			// CSSM_PADDING_PKCS1, etc. 
	CSSM_DATA_PTR iv,				// may be NULL
	uint32 effectiveKeySizeInBits,	// may be 0
	CSSM_DATA_PTR ctext,			// RETURNED
	CSSM_BOOL quiet)
{
	CSSM_CC_HANDLE cryptHand;
	CSSM_RETURN crtn;
	CSSM_SIZE bytesEncrypted;
	CSSM_DATA remData;
	int rtn;
	#if 	SAFE_CTEXT_ARRAY
	CSSM_DATA safeCtext[2];
	safeCtext[0] = *ctext;
	safeCtext[1].Data = NULL;
	safeCtext[1].Length = 10;		// lie, but shouldn't use this!
	#else
	//	printf("+++ ctext[0] = %d:0x%x; ctext[1] = %d:0x%x\n",
	//		ctext[0].Length, ctext[0].Data,
	//		ctext[1].Length, ctext[1].Data);
	#endif
	
	cryptHand = genCryptHandle(cspHand,
		alg,
		mode,
		padding,		
		key,
		NULL,			// no 2nd key
		iv,				// InitVector
		effectiveKeySizeInBits,
		0);				// rounds
	if(cryptHand == 0) {
		return testError(quiet);
	}
	
	remData.Data = NULL;
	remData.Length = 0;
	crtn = CSSM_EncryptData(cryptHand,
		ptext,
		1,
		#if 	SAFE_CTEXT_ARRAY
		&safeCtext[0],
		#else
		ctext,
		#endif
		1,
		&bytesEncrypted,
		&remData);
	#if 	SAFE_CTEXT_ARRAY
	*ctext = safeCtext[0];
	#endif
	
	if(crtn) {
		printError("CSSM_EncryptData", crtn);
		rtn = testError(quiet);
		goto done;
	}
	if(remData.Length != 0) {
		//printf("***WARNING: nonzero remData on encrypt!\n");
		/* new for CDSA2 - possible remData even if we ask the CSP to 
		 * malloc ctext */
		ctext->Data = (uint8 *)appRealloc(ctext->Data, bytesEncrypted, NULL);
		memmove(ctext->Data + ctext->Length, 
			remData.Data, 
			bytesEncrypted - ctext->Length);
		appFreeCssmData(&remData, CSSM_FALSE);
	}
	/* new for CDSA 2 */
	ctext->Length = bytesEncrypted;
	rtn = 0;
done:
	if(CSSM_DeleteContext(cryptHand)) {
		printError("CSSM_DeleteContext", 0);
		rtn = 1;
	}
	return rtn;
}

/*
 * Decrypt ctext using specified key, IV, effectiveKeySizeInBits
 */
static int decryptCom(CSSM_CSP_HANDLE cspHand,
	const char *testName,
	CSSM_DATA_PTR ctext,
	CSSM_KEY_PTR key,
	CSSM_ALGORITHMS alg,
	CSSM_ENCRYPT_MODE mode,
	CSSM_PADDING padding,		
	CSSM_DATA_PTR iv,				// may be NULL
	uint32 effectiveKeySizeInBits,	// may be 0
	CSSM_DATA_PTR ptext,			// RETURNED
	CSSM_BOOL quiet)
{
	CSSM_CC_HANDLE cryptHand;
	CSSM_RETURN crtn;
	CSSM_SIZE bytesDecrypted;
	CSSM_DATA remData;
	int rtn;

	cryptHand = genCryptHandle(cspHand,
		alg,
		mode,
		padding,		
		key,
		NULL,			// no 2nd key
		iv,				// InitVector
		effectiveKeySizeInBits,
		0);				// rounds
	if(cryptHand == 0) {
		return testError(quiet);
	}
	remData.Data = NULL;
	remData.Length = 0;
	crtn = CSSM_DecryptData(cryptHand,
		ctext,
		1,
		ptext,
		1,
		&bytesDecrypted,
		&remData);
	if(crtn) {
		printError("CSSM_DecryptData", crtn);
		rtn = testError(quiet);
		goto done;
	}
	if(remData.Length != 0) {
		//printf("***WARNING: nonzero remData on decrypt!\n");
		/* new for CDSA2 - possible remData even if we ask the CSP to 
		 * malloc ptext */
		ptext->Data = (uint8 *)appRealloc(ptext->Data, bytesDecrypted, NULL);
		memmove(ptext->Data + ptext->Length, 
			remData.Data, 
			bytesDecrypted - ptext->Length);
		appFreeCssmData(&remData, CSSM_FALSE);
	}
	/* new for CDSA 2 */
	ptext->Length = bytesDecrypted;
	rtn = 0;
done:
	if(CSSM_DeleteContext(cryptHand)) {
		printError("CSSM_DeleteContext", 0);
		rtn = 1;
	}
	return rtn;
}

/*
 * Common test portion
 *   encrypt ptext with key1, iv1
 *   encrypt ptext with key2, iv2
 *	 compare 2 ctexts; expect failure;
 */
 
#define TRAP_WRITER_ERR	1

static int testCommon(CSSM_CSP_HANDLE cspHand,
	const char *testName,
	CSSM_ALGORITHMS encrAlg,
	CSSM_ENCRYPT_MODE encrMode,
	CSSM_PADDING encrPad,
	uint32 effectiveKeySizeInBits,
	CSSM_DATA_PTR ptext,
	CSSM_KEY_PTR key1,
	CSSM_DATA_PTR iv1,
	CSSM_KEY_PTR key2,
	CSSM_DATA_PTR iv2,
	CSSM_BOOL quiet)
{
	CSSM_DATA		ctext1;
	CSSM_DATA		ctext2;
	ctext1.Data = NULL;
	ctext1.Length = 0;
	ctext2.Data = NULL;
	ctext2.Length = 0;
	if(encryptCom(cspHand,
			testName,
			ptext,
			key1,
			encrAlg,
			encrMode,
			encrPad,
			iv1,
			effectiveKeySizeInBits,
			&ctext1,
			quiet)) {
		return 1;
	}
	#if	TRAP_WRITER_ERR
	if(ctext2.Data != NULL){
		printf("Hey! encryptCom(ptext, ctext1 modified ctext2!\n");
		if(testError(quiet)) {
			return 1;
		}
	}
	#endif
	if(encryptCom(cspHand,
			testName,
			ptext,
			key2,
			encrAlg,
			encrMode,
			encrPad,
			iv2,
			effectiveKeySizeInBits,
			&ctext2,
			quiet)) {
		return 1;
	}
	if(compareData(&ctext1, &ctext2)) {
		printf("***%s: Unexpected Data compare!\n", testName);
		return testError(quiet);
	}
	appFreeCssmData(&ctext1, CSSM_FALSE);
	appFreeCssmData(&ctext2, CSSM_FALSE);
	return 0;
}

/**
 ** inidividual tests.
 **/
#define KEY_LABEL1		"noLabel1"
#define KEY_LABEL2		"noLabel2"
#define KEY_LABEL_LEN	strlen(KEY_LABEL1)
#define REPEAT_ON_ERROR	1

/* test repeatability - the only test here which actually decrypts */
static int repeatTest(testArgs *targs)
{
	/*
	generate two keys with same params;
	encrypt ptext with key1;
	decrypt ctext with key2;
	compare; expect success;
	*/
	CSSM_KEY_PTR	key1;
	CSSM_KEY_PTR	key2;
	CSSM_DATA		iv1;
	CSSM_DATA		iv2;
	CSSM_DATA_PTR	ivp1;
	CSSM_DATA_PTR	ivp2;
	CSSM_DATA		ctext;
	CSSM_DATA		rptext;
	CSSM_BOOL		gotErr = CSSM_FALSE;
	
	if(targs->useInitVector) {
		if(targs->genInitVector) {
			ivp1 = &iv1;
			ivp2 = &iv2;
		}
		else {
			staticIv.Length = targs->ivSize;
			ivp1 = ivp2 = &staticIv;
		}
	}
	else {
		ivp1 = ivp2 = NULL;
	}
	/* these need to be init'd regardless */
	iv1.Data = NULL;
	iv1.Length = 0;
	iv2.Data = NULL;
	iv2.Length = 0;
	ctext.Data = NULL;
	ctext.Length = 0;
	rptext.Data = NULL;
	rptext.Length = 0;
repeatDerive:
	key1 = cspDeriveKey(targs->cspHand,
		targs->deriveAlg,
		targs->keyAlg,
		KEY_LABEL1,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_ENCRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,
		targs->salt,
		targs->iterCount,
		&iv1);
	if(key1 == NULL) {
		return testError(targs->quiet);
	}
	key2 = cspDeriveKey(targs->cspHand,
		targs->deriveAlg,
		targs->keyAlg,
		KEY_LABEL2,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_DECRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,
		targs->salt,
		targs->iterCount,
		&iv2);
	if(key2 == NULL) {
		return testError(targs->quiet);
	}
repeatEnc:
	if(encryptCom(targs->cspHand,
			"repeatTest",
			targs->ptext,
			key1,
			targs->encrAlg,
			targs->encrMode,
			targs->encrPad,
			ivp1,
			targs->effectiveKeySizeInBits,
			&ctext,
			targs->quiet)) {
		return 1;
	}
	if(decryptCom(targs->cspHand,
			"repeatTest",
			&ctext,
			key2,
			targs->encrAlg,
			targs->encrMode,
			targs->encrPad,
			ivp2,
			targs->effectiveKeySizeInBits,
			&rptext,
			targs->quiet)) {
		return 1;
	}
	if(gotErr || !compareData(targs->ptext, &rptext)) {
		printf("***Data miscompare in repeatTest\n");
		if(REPEAT_ON_ERROR) {
			char str;
			
			gotErr = CSSM_TRUE;
			fpurge(stdin);
			printf("Repeat enc/dec (r), repeat derive (d), continue (c), abort (any)? ");
			str = getchar();
			switch(str) {
				case 'r':
					appFreeCssmData(&ctext, CSSM_FALSE);	
					appFreeCssmData(&rptext, CSSM_FALSE);	
					goto repeatEnc;
				case 'd':
					appFreeCssmData(&ctext, CSSM_FALSE);	
					appFreeCssmData(&rptext, CSSM_FALSE);	
					appFreeCssmData(&iv1, CSSM_FALSE);
					appFreeCssmData(&iv2, CSSM_FALSE);
					cspFreeKey(targs->cspHand, key1);
					cspFreeKey(targs->cspHand, key2);
					goto repeatDerive;
				case 'c':
					break;
				default:
					return 1;
			}
		}
		else {
			return testError(targs->quiet);
		}
	}
	appFreeCssmData(&ctext, CSSM_FALSE);
	appFreeCssmData(&rptext, CSSM_FALSE);
	appFreeCssmData(&iv1, CSSM_FALSE);
	appFreeCssmData(&iv2, CSSM_FALSE);
	cspFreeKey(targs->cspHand, key1);
	cspFreeKey(targs->cspHand, key2);
	CSSM_FREE(key1);
	CSSM_FREE(key2);
	return 0;
}

/* ensure iterCount alters key */
static int iterTest(testArgs *targs)
{
	/*
	generate key1(iterCount), key2(iterCount+1);
	encrypt ptext with key1;
	encrypt ptext with key2;
	compare 2 ctexts; expect failure;
	*/
	CSSM_KEY_PTR	key1;
	CSSM_KEY_PTR	key2;
	CSSM_DATA		iv1;
	CSSM_DATA		iv2;
	CSSM_DATA_PTR	ivp1;
	CSSM_DATA_PTR	ivp2;
	if(targs->useInitVector) {
		if(targs->genInitVector) {
			ivp1 = &iv1;
			ivp2 = &iv2;
		}
		else {
			staticIv.Length = targs->ivSize;
			ivp1 = ivp2 = &staticIv;
		}
	}
	else {
		ivp1 = ivp2 = NULL;
	}
	/* these need to be init'd regardless */
	iv1.Data = NULL;
	iv1.Length = 0;
	iv2.Data = NULL;
	iv2.Length = 0;
	key1 = cspDeriveKey(targs->cspHand,
		targs->deriveAlg,
		targs->keyAlg,
		KEY_LABEL1,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_ENCRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,
		targs->salt,
		targs->iterCount,
		&iv1);
	if(key1 == NULL) {
		return testError(targs->quiet);
	}
	key2 = cspDeriveKey(targs->cspHand,
		targs->deriveAlg,
		targs->keyAlg,
		KEY_LABEL2,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_ENCRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,
		targs->salt,
		targs->iterCount + 1,		// the changed param
		&iv2);
	if(key2 == NULL) {
		return testError(targs->quiet);
	}
	if(testCommon(targs->cspHand,
				"iterTest",
				targs->encrAlg,
				targs->encrMode,
				targs->encrPad,
				targs->effectiveKeySizeInBits,
				targs->ptext,
			 	key1,
				ivp1,
				key2,
				ivp2,
				targs->quiet)) {
		return 1;
	}
	appFreeCssmData(&iv1, CSSM_FALSE);
	appFreeCssmData(&iv2, CSSM_FALSE);
	cspFreeKey(targs->cspHand, key1);
	cspFreeKey(targs->cspHand, key2);
	CSSM_FREE(key1);
	CSSM_FREE(key2);
	return 0;
}

/* ensure password alters key */
static int passwordTest(testArgs *targs)
{
	/*
	generate key1(password), key2(munged password);
	encrypt ptext with key1;
	encrypt ptext with key2;
	compare 2 ctexts; expect failure;
	*/
	CSSM_KEY_PTR	key1;
	CSSM_KEY_PTR	key2;
	CSSM_DATA		iv1;
	CSSM_DATA		iv2;
	CSSM_DATA_PTR	ivp1;
	CSSM_DATA_PTR	ivp2;
	uint32			mungeDex;
	uint32			mungeBits;
	if(targs->useInitVector) {
		if(targs->genInitVector) {
			ivp1 = &iv1;
			ivp2 = &iv2;
		}
		else {
			staticIv.Length = targs->ivSize;
			ivp1 = ivp2 = &staticIv;
		}
	}
	else {
		ivp1 = ivp2 = NULL;
	}
	/* these need to be init'd regardless */
	iv1.Data = NULL;
	iv1.Length = 0;
	iv2.Data = NULL;
	iv2.Length = 0;
	key1 = cspDeriveKey(targs->cspHand,
		targs->deriveAlg,
		targs->keyAlg,
		KEY_LABEL1,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_ENCRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,
		targs->salt,
		targs->iterCount,
		&iv1);
	if(key1 == NULL) {
		return testError(targs->quiet);
	}
	/* munge password */
	mungeDex = genRand(0, targs->password->Length - 1);
	mungeBits = randBit();
	targs->password->Data[mungeDex] ^= mungeBits;
	key2 = cspDeriveKey(targs->cspHand,
		targs->deriveAlg,
		targs->keyAlg,
		KEY_LABEL2,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_ENCRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,			// the changed param
		targs->salt,
		targs->iterCount,
		&iv2);
	if(key2 == NULL) {
		return testError(targs->quiet);
	}
	if(testCommon(targs->cspHand,
				"passwordTest",
				targs->encrAlg,
				targs->encrMode,
				targs->encrPad,
				targs->effectiveKeySizeInBits,
				targs->ptext,
			 	key1,
				ivp1,
				key2,
				ivp2,
				targs->quiet)) {
		return 1;
	}
	/* restore  */
	targs->password->Data[mungeDex] ^= mungeBits;
	appFreeCssmData(&iv1, CSSM_FALSE);
	appFreeCssmData(&iv2, CSSM_FALSE);
	cspFreeKey(targs->cspHand, key1);
	cspFreeKey(targs->cspHand, key2);
	CSSM_FREE(key1);
	CSSM_FREE(key2);
	return 0;
}

/* ensure salt alters key */
static int saltTest(testArgs *targs)
{
	/*
	generate key1(seed), key2(munged seed);
	encrypt ptext with key1;
	encrypt ptext with key2;
	compare 2 ctexts; expect failure;
	*/
	CSSM_KEY_PTR	key1;
	CSSM_KEY_PTR	key2;
	CSSM_DATA		iv1;
	CSSM_DATA		iv2;
	CSSM_DATA_PTR	ivp1;
	CSSM_DATA_PTR	ivp2;
	uint32			mungeDex;
	uint32			mungeBits;
	if(targs->useInitVector) {
		if(targs->genInitVector) {
			ivp1 = &iv1;
			ivp2 = &iv2;
		}
		else {
			staticIv.Length = targs->ivSize;
			ivp1 = ivp2 = &staticIv;
		}
	}
	else {
		ivp1 = ivp2 = NULL;
	}
	/* these need to be init'd regardless */
	iv1.Data = NULL;
	iv1.Length = 0;
	iv2.Data = NULL;
	iv2.Length = 0;
	key1 = cspDeriveKey(targs->cspHand,
		targs->deriveAlg,
		targs->keyAlg,
		KEY_LABEL1,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_ENCRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,
		targs->salt,
		targs->iterCount,
		&iv1);
	if(key1 == NULL) {
		return testError(targs->quiet);
	}
	/* munge salt */
	mungeDex = genRand(0, targs->salt->Length - 1);
	mungeBits = randBit();
	targs->salt->Data[mungeDex] ^= mungeBits;
	key2 = cspDeriveKey(targs->cspHand,
		targs->deriveAlg,
		targs->keyAlg,
		KEY_LABEL2,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_ENCRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,
		targs->salt,				// the changed param
		targs->iterCount,
		&iv2);
	if(key2 == NULL) {
		return testError(targs->quiet);
	}
	if(testCommon(targs->cspHand,
				"saltTest",
				targs->encrAlg,
				targs->encrMode,
				targs->encrPad,
				targs->effectiveKeySizeInBits,
				targs->ptext,
			 	key1,
				ivp1,
				key2,
				ivp2,
				targs->quiet)) {
		return 1;
	}
	/* restore  */
	targs->salt->Data[mungeDex] ^= mungeBits;
	appFreeCssmData(&iv1, CSSM_FALSE);
	appFreeCssmData(&iv2, CSSM_FALSE);
	cspFreeKey(targs->cspHand, key1);
	cspFreeKey(targs->cspHand, key2);
	CSSM_FREE(key1);
	CSSM_FREE(key2);
	return 0;
}

/* ensure initVector alters ctext. This isn't testing PBE per se, but
 * it's a handy place to verify this function. */
static int initVectTest(testArgs *targs)
{
	/*
	generate key1;
	encrypt ptext with key1 and initVector;
	encrypt ptext with key1 and munged initVector;
	compare 2 ctexts; expect failure;
	*/
	CSSM_KEY_PTR	key1;
	CSSM_DATA		iv1;
	CSSM_DATA		iv2;
	uint32			mungeDex;
	uint32			mungeBits;
	
	if(targs->genInitVector) {
		iv1.Data = NULL;
		iv1.Length = 0;
	}
	else  {
		iv1 = staticIv;
	}
	key1 = cspDeriveKey(targs->cspHand,
		targs->deriveAlg,
		targs->keyAlg,
		KEY_LABEL1,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_ENCRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,
		targs->salt,
		targs->iterCount,
		&iv1);
	if(key1 == NULL) {
		return testError(targs->quiet);
	}
	
	/* get munged copy of iv */
	iv2.Data = (uint8 *)CSSM_MALLOC(iv1.Length);
	iv2.Length = iv1.Length;
	memmove(iv2.Data, iv1.Data, iv1.Length);
	mungeDex = genRand(0, iv1.Length - 1);
	mungeBits = randBit();
	iv2.Data[mungeDex] ^= mungeBits;
	if(testCommon(targs->cspHand,
				"initVectTest",
				targs->encrAlg,
				targs->encrMode,
				targs->encrPad,
				targs->effectiveKeySizeInBits,
				targs->ptext,
			 	key1,
				&iv1,
				key1,
				&iv2,	// the changed param
				targs->quiet)) {	
		return 1;
	}
	if(targs->genInitVector) {
		appFreeCssmData(&iv1, CSSM_FALSE);
	}
	appFreeCssmData(&iv2, CSSM_FALSE);
	cspFreeKey(targs->cspHand, key1);
	CSSM_FREE(key1);
	return 0;
}

#if 0
/* only one algorithm supported */
/* ensure deriveAlg alters key */
static int deriveAlgTest(testArgs *targs)
{
	/*
	generate key1(deriveAlg), key2(some other deriveAlg);
	encrypt ptext with key1;
	encrypt ptext with key2;
	compare 2 ctexts; expect failure;
	*/
	CSSM_KEY_PTR    key1;
	CSSM_KEY_PTR    key2;
	CSSM_DATA       iv1;
	CSSM_DATA       iv2;
	CSSM_DATA_PTR   ivp1;
	CSSM_DATA_PTR   ivp2;
	uint32          mungeAlg;
	
	if(targs->useInitVector) {
		if(targs->genInitVector) {
			ivp1 = &iv1;
			ivp2 = &iv2;
		}
		else {
			staticIv.Length = targs->ivSize;
			ivp1 = ivp2 = &staticIv;
		}
	}
	else {
		ivp1 = ivp2 = NULL;
	}
	
	/* these need to be init'd regardless */
	iv1.Data = NULL;
	iv1.Length = 0;
	iv2.Data = NULL;
	iv2.Length = 0;
	key1 = cspDeriveKey(targs->cspHand,
		targs->deriveAlg,
		targs->keyAlg,
		KEY_LABEL1,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_ENCRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,
		targs->salt,
		targs->iterCount,
		&iv1);
	if(key1 == NULL) {
		return testError(quiet);
	}
	
	/* munge deriveAlg */
	switch(targs->deriveAlg) {
		case CSSM_ALGID_MD5_PBE:
			mungeAlg = CSSM_ALGID_MD2_PBE;
			break;
		case CSSM_ALGID_MD2_PBE:
			mungeAlg = CSSM_ALGID_SHA1_PBE;
			break;
		case CSSM_ALGID_SHA1_PBE:
			mungeAlg = CSSM_ALGID_SHA1_PBE_PKCS12;
			break;
		case CSSM_ALGID_SHA1_PBE_PKCS12:
			mungeAlg = CSSM_ALGID_MD5_PBE;
			break;
		default:
			printf("BRRRZZZT! Update deriveAlgTest()!\n");
			return testError(quiet);
	}
	key2 = cspDeriveKey(targs->cspHand,
		mungeAlg,                               // the changed param
		targs->keyAlg,
		KEY_LABEL2,
		KEY_LABEL_LEN,
		CSSM_KEYUSE_ENCRYPT,
		targs->keySizeInBits,
		targs->useRefKey,
		targs->password,                        // the changed param
		targs->salt,
		targs->iterCount,
		&iv2);
	if(key2 == NULL) {
		return testError(quiet);
	}
	if(testCommon(targs->cspHand,
			"deriveAlgTest",
			targs->encrAlg,
			targs->encrMode,
			targs->encrPad,
			targs->effectiveKeySizeInBits,
			targs->ptext,
			key1,
			ivp1,
			key2,
			ivp2,
			targs->quiet)) {
		return 1;
	}
	appFreeCssmData(&iv1, CSSM_FALSE);
	appFreeCssmData(&iv2, CSSM_FALSE);
	cspFreeKey(targs->cspHand, key1);
	cspFreeKey(targs->cspHand, key2);
	CSSM_FREE(key1);
	CSSM_FREE(key2);
	return 0;
}
#endif

int main(int argc, char **argv)
{
	int					arg;
	char				*argp;
	unsigned			loop;
	CSSM_DATA			ptext;
	testArgs			targs;
	CSSM_DATA			pwd;
	CSSM_DATA			salt;
	privAlg				pbeAlg;
	privAlg				encrAlg;
	privAlg				lastEncrAlg;
	int 				rtn = 0;
	CSSM_BOOL 			fooBool;
	CSSM_BOOL			refKeysOnly = CSSM_FALSE;
	int					i;
	
	/*
	 * User-spec'd params
	 */
	unsigned	loops = LOOPS_DEF;
	CSSM_BOOL	quiet = CSSM_FALSE;
	CSSM_BOOL	doPause = CSSM_FALSE;
	CSSM_BOOL	doExport = CSSM_FALSE;
	CSSM_BOOL	repeatOnly = CSSM_FALSE;
	CSSM_BOOL	bareCsp = CSSM_TRUE;
	CSSM_BOOL	zeroLenPassword = CSSM_FALSE;
	
	#if	macintosh
	argc = ccommand(&argv);
	#endif
	for(arg=1; arg<argc; arg++) {
		argp = argv[arg];
		switch(argp[0]) {
		    case 'l':
				loops = atoi(&argp[2]);
				break;
		    case 'q':
		    	quiet = CSSM_TRUE;
				break;
			case 'D':
				bareCsp = CSSM_FALSE;
				#if CSPDL_ALL_KEYS_ARE_REF
				refKeysOnly = CSSM_TRUE;
				#endif
				break;
		    case 'p':
		    	doPause = CSSM_TRUE;
				break;
			case 'e':
				doExport = CSSM_TRUE;
				break;
			case 'r':
				repeatOnly = CSSM_TRUE;
				break;
			case 'z':
				zeroLenPassword = CSSM_TRUE;
				break;
		    case 'h':
		    default:
				usage(argv);
		}
	}

	/* statically allocate ptext, password and seed; data and length
	 * change in test loop */
	pwd.Data = (uint8 *)CSSM_MALLOC(MAX_PASSWORD_SIZE);
	ptext.Data = (uint8 *)CSSM_MALLOC(MAX_PTEXT_SIZE);
	salt.Data = (uint8 *)CSSM_MALLOC(MAX_SALT_SIZE);
	printf("Starting pbeTest; args: ");
	for(i=1; i<argc; i++) {
		printf("%s ", argv[i]);
	}
	printf("\n");
	targs.cspHand = cspDlDbStartup(bareCsp, NULL);
	if(targs.cspHand == 0) {
		exit(1);
	}
	targs.ptext 	= &ptext;
	targs.password 	= &pwd;
	targs.salt 		= &salt;
	targs.quiet     = quiet;
	if(doExport) {
		lastEncrAlg = ENCR_ALG_LAST_EXPORT;
	}
	else {
		lastEncrAlg = ENCR_ALG_LAST;
	}
	for(loop=1; ; loop++) {
		if(!quiet) {
			printf("...loop %d\n", loop);
		}
		/* change once per outer loop */
		simpleGenData(&ptext, MIN_PTEXT_SIZE, MAX_PTEXT_SIZE);
		if(zeroLenPassword) {
			pwd.Length = 0;	// fixed
		}
		else {
			simpleGenData(&pwd, APPLE_PBE_MIN_PASSWORD, MAX_PASSWORD_SIZE);
		}
		simpleGenData(&salt, APPLE_PBE_MIN_SALT, MAX_SALT_SIZE);
		targs.iterCount = genRand(MIN_ITER_COUNT, MAX_ITER_COUNT);
		if(refKeysOnly) {
			targs.useRefKey = CSSM_TRUE;
		}
		else {
			targs.useRefKey = (loop & 1) ? CSSM_FALSE : CSSM_TRUE;
		}
		
		for(encrAlg=ENCR_ALG_FIRST; encrAlg<=lastEncrAlg; encrAlg++) {
			/* Cook up encryption-related args */
			algInfo(encrAlg,
				&targs.keyAlg,
				&targs.encrAlg,
				&targs.encrMode,
				&targs.encrPad,
				&targs.useInitVector,
				&targs.ivSize,
				&fooBool,				// genInitVector
				&targs.keyAlgStr);
			/* random key size */
			targs.effectiveKeySizeInBits = randKeySizeBits(targs.keyAlg, OT_Encrypt);
			targs.keySizeInBits = (targs.effectiveKeySizeInBits + 7) & ~7;
			if(targs.keySizeInBits == targs.effectiveKeySizeInBits) {
				/* same size, ignore effective */
				targs.effectiveKeySizeInBits = 0;
			}
			if(!quiet) {
				printf("   ...Encrypt alg %s keySizeInBits %u effectKeySize %u\n",
					targs.keyAlgStr, (unsigned)targs.keySizeInBits,
					(unsigned)targs.effectiveKeySizeInBits);
			}
			for(pbeAlg=PBE_ALG_FIRST; pbeAlg<=PBE_ALG_LAST; pbeAlg++) {
				/* Cook up pbe-related args */
				uint32 foo;
				algInfo(pbeAlg,
					&targs.deriveAlg,
					&foo, 			// encrAlg
					&foo,			// mode
					&foo,
					&fooBool,		// useInitVector
					&foo,			// ivSize
					&targs.genInitVector,
					&targs.deriveAlgStr);
				if(!quiet) {
					printf("      ...PBE alg %s\n", targs.deriveAlgStr);
				}
				/* grind thru the tests */
				if(repeatTest(&targs)) {
					rtn = 1;
					goto testDone;
				}
				if(repeatOnly) {
					continue;
				}
				if(iterTest(&targs)) {
					rtn = 1;
					goto testDone;
				}
				#if 0
				// not supported yet 
				if(deriveAlgTest(&targs)) {
					rtn = 1;
					goto testDone;
				}
				#endif
				if(!zeroLenPassword) {
					/* won't work with zero length password */
					if(passwordTest(&targs)) {
						rtn = 1;
						goto testDone;
					}
				}
				if(saltTest(&targs)) {
					rtn = 1;
					goto testDone;
				}
				if(targs.useInitVector) {
					if(initVectTest(&targs)) {
						rtn = 1;
						goto testDone;
					}
				}
			} /* for pbeAlg */
		} /* for encrAlg */
		if(doPause) {
			if(testError(quiet)) {
				break;
			}
		}
		if(loops && (loop == loops)) {
			break;
		}
	} /* for loop */
	
testDone:
	CSSM_ModuleDetach(targs.cspHand);
	if(!quiet && (rtn == 0)) {
		printf("%s test complete\n", argv[0]);
	}
	return rtn;
}