cspwrap.c   [plain text]


/* Copyright 1997 Apple Computer, Inc.
 *
 * cspwrap.c - wrappers to simplify access to CDSA
 *
 * Revision History
 * ----------------
 *   3 May 2000 Doug Mitchell
 *		Ported to X/CDSA2.
 *  12 Aug 1997	Doug Mitchell at Apple
 *		Created.
 */
 
#include <Security/cssmapple.h>
#include <Security/cssm.h>
#include "cspwrap.h"
#include "common.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* MCF hack */
// #include <CarbonCore/MacTypes.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h>
/* end MCF */

#ifndef	NULL
#define NULL ((void *)0)
#endif	/* NULL */
#ifndef	MAX
#define MAX(a,b)	((a > b) ? a : b)
#define MIN(a,b)	((a < b) ? a : b)
#endif

#pragma mark --------- Key Generation ---------

/*
 * Key generation
 */
#define FEE_PRIV_DATA_SIZE	20
/*
 * Debug/test only. BsafeCSP only (long since disabled, in Puma).
 * This results in quicker but less secure RSA key generation.
 */
#define RSA_WEAK_KEYS		0

/*
 * Force bad data in KeyData prior to generating, deriving, or
 * wrapping key to ensure that the CSP ignores incoming
 * KeyData.
 */
static void setBadKeyData(
	CSSM_KEY_PTR key)
{
	key->KeyData.Data = (uint8 *)0xeaaaeaaa;	// bad ptr
	key->KeyData.Length = 1;	// no key can fit here
}

/*
 * Generate key pair of arbitrary algorithm. 
 * FEE keys will have random private data.
 */
CSSM_RETURN cspGenKeyPair(CSSM_CSP_HANDLE cspHand,
	uint32 algorithm,
	const char *keyLabel,
	unsigned keyLabelLen,
	uint32 keySize,					// in bits
	CSSM_KEY_PTR pubKey,			// mallocd by caller
	CSSM_BOOL pubIsRef,				// true - reference key, false - data
	uint32 pubKeyUsage,				// CSSM_KEYUSE_ENCRYPT, etc.
	CSSM_KEYBLOB_FORMAT pubFormat,	// Optional. Specify 0 or CSSM_KEYBLOB_RAW_FORMAT_NONE
									//   to get the default format. 
	CSSM_KEY_PTR privKey,			// mallocd by caller
	CSSM_BOOL privIsRef,			// true - reference key, false - data
	uint32 privKeyUsage,			// CSSM_KEYUSE_DECRYPT, etc.
	CSSM_KEYBLOB_FORMAT privFormat,	// optional 0 ==> default
	CSSM_BOOL genSeed)				// FEE only. True: we generate seed and CSP
									// will hash it. False: CSP generates random 
									// seed. 
{
	CSSM_RETURN				crtn;
	CSSM_CC_HANDLE 			ccHand;
	CSSM_DATA				privData = {0, NULL};		// mallocd for FEE
	CSSM_CRYPTO_DATA		privCData;
	CSSM_CRYPTO_DATA_PTR	privCDataPtr = NULL;
	CSSM_DATA				keyLabelData;
	uint32					pubAttr;
	uint32					privAttr;
	CSSM_RETURN 			ocrtn = CSSM_OK;
	
	/* pre-context-create algorithm-specific stuff */
	switch(algorithm) {
		case CSSM_ALGID_FEE:
			if(genSeed) {
				/* cook up random privData */
				privData.Data = (uint8 *)CSSM_MALLOC(FEE_PRIV_DATA_SIZE);
				privData.Length = FEE_PRIV_DATA_SIZE;
				appGetRandomBytes(privData.Data, FEE_PRIV_DATA_SIZE);
				privCData.Param = privData;
				privCData.Callback = NULL;
				privCDataPtr = &privCData;
			}
			/* else CSP generates random seed/key */
			
			if(keySize == CSP_KEY_SIZE_DEFAULT) {
				keySize = CSP_FEE_KEY_SIZE_DEFAULT;
			}
			break;
		case CSSM_ALGID_RSA:
			if(keySize == CSP_KEY_SIZE_DEFAULT) {
				keySize = CSP_RSA_KEY_SIZE_DEFAULT;
			}
			break;
		case CSSM_ALGID_DSA:
			if(keySize == CSP_KEY_SIZE_DEFAULT) {
				keySize = CSP_DSA_KEY_SIZE_DEFAULT;
			}
			break;
		default:
			printf("cspGenKeyPair: Unknown algorithm\n");
			/* but what the hey */
			privCDataPtr = NULL;
			break;
	}
	keyLabelData.Data        = (uint8 *)keyLabel,
	keyLabelData.Length      = keyLabelLen;
	memset(pubKey, 0, sizeof(CSSM_KEY));
	memset(privKey, 0, sizeof(CSSM_KEY));
	setBadKeyData(pubKey);
	setBadKeyData(privKey);
	
	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
		algorithm,
		keySize,
		privCDataPtr,			// Seed
		NULL,					// Salt
		NULL,					// StartDate
		NULL,					// EndDate
		NULL,					// Params
		&ccHand);
	if(crtn) {
		printError("CSSM_CSP_CreateKeyGenContext", crtn);
		ocrtn = crtn;
		goto abort;
	}
	/* cook up attribute bits */
	if(pubIsRef) {
		pubAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
	}
	else {
		pubAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	}
	if(privIsRef) {
		privAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
	}
	else {
		privAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	}

	/* post-context-create algorithm-specific stuff */
	switch(algorithm) {
		case CSSM_ALGID_RSA:
		
			#if	RSA_WEAK_KEYS
			{
				/* for testing, speed up key gen by using the
				* undocumented "CUSTOM" key gen mode. This
				* results in the CSP using AI_RsaKeyGen instead of
				* AI_RSAStrongKeyGen.
				*/
				crtn = AddContextAttribute(ccHand,
					CSSM_ATTRIBUTE_MODE,
					sizeof(uint32),		
					CAT_Uint32,
					NULL,
					CSSM_ALGMODE_CUSTOM);
				if(crtn) {
					printError("CSSM_UpdateContextAttributes", crtn);
					return crtn;
				}
			}
			#endif	// RSA_WEAK_KEYS
			break;
		 
		 case CSSM_ALGID_DSA:
			/* 
			 * extra step - generate params - this just adds some
			 * info to the context
			 */
			{
				CSSM_DATA dummy = {0, NULL};
				crtn = CSSM_GenerateAlgorithmParams(ccHand, 
					keySize, &dummy);
				if(crtn) {
					printError("CSSM_GenerateAlgorithmParams", crtn);
					return crtn;
				}
				appFreeCssmData(&dummy, CSSM_FALSE);
			}
			break;
		default:
			break;
	}
	
	/* optional format specifiers */
	if(!pubIsRef && (pubFormat != CSSM_KEYBLOB_RAW_FORMAT_NONE)) {
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
			sizeof(uint32),	
			CAT_Uint32,
			NULL,
			pubFormat);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT)", crtn);
			return crtn;
		}
	}
	if(!privIsRef && (privFormat != CSSM_KEYBLOB_RAW_FORMAT_NONE)) {
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT,
			sizeof(uint32),			// currently sizeof CSSM_DATA
			CAT_Uint32,
			NULL,
			privFormat);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT)", crtn);
			return crtn;
		}
	}
	crtn = CSSM_GenerateKeyPair(ccHand,
		pubKeyUsage,
		pubAttr,
		&keyLabelData,
		pubKey,
		privKeyUsage,
		privAttr,
		&keyLabelData,			// same labels
		NULL,					// CredAndAclEntry
		privKey);
	if(crtn) {
		printError("CSSM_GenerateKeyPair", crtn);
		ocrtn = crtn;
		goto abort;
	}
	/* basic checks...*/
	if(privIsRef) {
		if(privKey->KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE) {
			printf("privKey blob type: exp %u got %u\n",
				CSSM_KEYBLOB_REFERENCE, (unsigned)privKey->KeyHeader.BlobType);
			ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
			goto abort;
		}
	}
	else {
		switch(privKey->KeyHeader.BlobType) {
			case CSSM_KEYBLOB_RAW:
				break;
			default:
				printf("privKey blob type: exp raw, got %u\n",
					(unsigned)privKey->KeyHeader.BlobType);
				ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
				goto abort;
		}
	}
	if(pubIsRef) {
		if(pubKey->KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE) {
			printf("pubKey blob type: exp %u got %u\n",
				CSSM_KEYBLOB_REFERENCE, (unsigned)pubKey->KeyHeader.BlobType);
			ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
			goto abort;
		}
	}
	else {
		switch(pubKey->KeyHeader.BlobType) {
			case CSSM_KEYBLOB_RAW:
				break;
			default:
				printf("pubKey blob type: exp raw or raw_berder, got %u\n",
					(unsigned)pubKey->KeyHeader.BlobType);
				ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
				goto abort;
		}
	}
abort:
	if(ccHand != 0) {
		crtn = CSSM_DeleteContext(ccHand);
		if(crtn) {
			printError("CSSM_DeleteContext", crtn);
			ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
		}
	}
	if(privData.Data != NULL) {
		CSSM_FREE(privData.Data);
	}
	return ocrtn;
}

/*
 * Generate FEE key pair with optional primeType, curveType, and seed (password) data.
 */
CSSM_RETURN cspGenFEEKeyPair(CSSM_CSP_HANDLE cspHand,
	const char *keyLabel,
	unsigned keyLabelLen,
	uint32 keySize,					// in bits
	uint32 primeType,				// CSSM_FEE_PRIME_TYPE_MERSENNE, etc.
	uint32 curveType,				// CSSM_FEE_CURVE_TYPE_MONTGOMERY, etc.
	CSSM_KEY_PTR pubKey,			// mallocd by caller
	CSSM_BOOL pubIsRef,				// true - reference key, false - data
	uint32 pubKeyUsage,				// CSSM_KEYUSE_ENCRYPT, etc.
	CSSM_KEYBLOB_FORMAT pubFormat,	// Optional. Specify 0 or CSSM_KEYBLOB_RAW_FORMAT_NONE
									//   to get the default format. 
	CSSM_KEY_PTR privKey,			// mallocd by caller
	CSSM_BOOL privIsRef,			// true - reference key, false - data
	uint32 privKeyUsage,			// CSSM_KEYUSE_DECRYPT, etc.
	CSSM_KEYBLOB_FORMAT privFormat,	// optional 0 ==> default
	const CSSM_DATA *seedData)		// Present: CSP will hash this for private data.
									// NULL: CSP generates random seed. 
{
	CSSM_RETURN				crtn;
	CSSM_CC_HANDLE 			ccHand;
	CSSM_CRYPTO_DATA		privCData;
	CSSM_CRYPTO_DATA_PTR	privCDataPtr = NULL;
	CSSM_DATA				keyLabelData;
	uint32					pubAttr;
	uint32					privAttr;
	CSSM_RETURN 			ocrtn = CSSM_OK;
	
	/* pre-context-create algorithm-specific stuff */
	if(seedData) {
		privCData.Param = *((CSSM_DATA_PTR)seedData);
		privCData.Callback = NULL;
		privCDataPtr = &privCData;
	}
	/* else CSP generates random seed/key */
	
	if(keySize == CSP_KEY_SIZE_DEFAULT) {
		keySize = CSP_FEE_KEY_SIZE_DEFAULT;
	}

	keyLabelData.Data        = (uint8 *)keyLabel,
	keyLabelData.Length      = keyLabelLen;
	memset(pubKey, 0, sizeof(CSSM_KEY));
	memset(privKey, 0, sizeof(CSSM_KEY));
	setBadKeyData(pubKey);
	setBadKeyData(privKey);
	
	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
		CSSM_ALGID_FEE,
		keySize,
		privCDataPtr,			// Seed
		NULL,					// Salt
		NULL,					// StartDate
		NULL,					// EndDate
		NULL,					// Params
		&ccHand);
	if(crtn) {
		printError("CSSM_CSP_CreateKeyGenContext", crtn);
		ocrtn = crtn;
		goto abort;
	}
	/* cook up attribute bits */
	if(pubIsRef) {
		pubAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
	}
	else {
		pubAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	}
	if(privIsRef) {
		privAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
	}
	else {
		privAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	}

	/* optional post-context-create stuff */
	if(primeType != CSSM_FEE_PRIME_TYPE_DEFAULT) {
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_FEE_PRIME_TYPE,
			sizeof(uint32),		
			CAT_Uint32,
			NULL,
			primeType);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_FEE_PRIME_TYPE)", crtn);
			return crtn;
		}
	}
	if(curveType != CSSM_FEE_CURVE_TYPE_DEFAULT) {
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_FEE_CURVE_TYPE,
			sizeof(uint32),		
			CAT_Uint32,
			NULL,
			curveType);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_FEE_CURVE_TYPE)", crtn);
			return crtn;
		}
	}
	
	if(pubFormat != CSSM_KEYBLOB_RAW_FORMAT_NONE) {
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
			sizeof(uint32),		
			CAT_Uint32,
			NULL,
			pubFormat);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT)", crtn);
			return crtn;
		}
	}
	if(privFormat != CSSM_KEYBLOB_RAW_FORMAT_NONE) {
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT,
			sizeof(uint32),			// currently sizeof CSSM_DATA
			CAT_Uint32,
			NULL,
			pubFormat);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT)", crtn);
			return crtn;
		}
	}
	crtn = CSSM_GenerateKeyPair(ccHand,
		pubKeyUsage,
		pubAttr,
		&keyLabelData,
		pubKey,
		privKeyUsage,
		privAttr,
		&keyLabelData,			// same labels
		NULL,					// CredAndAclEntry
		privKey);
	if(crtn) {
		printError("CSSM_GenerateKeyPair", crtn);
		ocrtn = crtn;
		goto abort;
	}
	/* basic checks...*/
	if(privIsRef) {
		if(privKey->KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE) {
			printf("privKey blob type: exp %u got %u\n",
				CSSM_KEYBLOB_REFERENCE, (unsigned)privKey->KeyHeader.BlobType);
			ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
			goto abort;
		}
	}
	else {
		switch(privKey->KeyHeader.BlobType) {
			case CSSM_KEYBLOB_RAW:
				break;
			default:
				printf("privKey blob type: exp raw, got %u\n",
					(unsigned)privKey->KeyHeader.BlobType);
				ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
				goto abort;
		}
	}
	if(pubIsRef) {
		if(pubKey->KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE) {
			printf("pubKey blob type: exp %u got %u\n",
				CSSM_KEYBLOB_REFERENCE, (unsigned)pubKey->KeyHeader.BlobType);
			ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
			goto abort;
		}
	}
	else {
		switch(pubKey->KeyHeader.BlobType) {
			case CSSM_KEYBLOB_RAW:
				break;
			default:
				printf("pubKey blob type: exp raw or raw_berder, got %u\n",
					(unsigned)pubKey->KeyHeader.BlobType);
				ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
				goto abort;
		}
	}
abort:
	if(ccHand != 0) {
		crtn = CSSM_DeleteContext(ccHand);
		if(crtn) {
			printError("CSSM_DeleteContext", crtn);
			ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
		}
	}
	return ocrtn;
}

/*
 * Generate DSA key pair with optional generateAlgParams and optional
 * incoming parameters.
 */
CSSM_RETURN cspGenDSAKeyPair(CSSM_CSP_HANDLE cspHand,
	const char *keyLabel,
	unsigned keyLabelLen,
	uint32 keySize,					// in bits
	CSSM_KEY_PTR pubKey,			// mallocd by caller
	CSSM_BOOL pubIsRef,				// true - reference key, false - data
	uint32 pubKeyUsage,				// CSSM_KEYUSE_ENCRYPT, etc.
	CSSM_KEYBLOB_FORMAT pubFormat,	// Optional. Specify 0 or CSSM_KEYBLOB_RAW_FORMAT_NONE
									//   to get the default format. 
	CSSM_KEY_PTR privKey,			// mallocd by caller
	CSSM_BOOL privIsRef,			// true - reference key, false - data
	uint32 privKeyUsage,			// CSSM_KEYUSE_DECRYPT, etc.
	CSSM_KEYBLOB_FORMAT privFormat,	// Optional. Specify 0 or CSSM_KEYBLOB_RAW_FORMAT_NONE
									//   to get the default format. 
	CSSM_BOOL genParams,
	CSSM_DATA_PTR paramData)		// optional	
{
	CSSM_RETURN				crtn;
	CSSM_CC_HANDLE 			ccHand;
	CSSM_DATA				keyLabelData;
	uint32					pubAttr;
	uint32					privAttr;
	CSSM_RETURN 			ocrtn = CSSM_OK;
	
	if(keySize == CSP_KEY_SIZE_DEFAULT) {
		keySize = CSP_DSA_KEY_SIZE_DEFAULT;
	}
	keyLabelData.Data        = (uint8 *)keyLabel,
	keyLabelData.Length      = keyLabelLen;
	memset(pubKey, 0, sizeof(CSSM_KEY));
	memset(privKey, 0, sizeof(CSSM_KEY));
	setBadKeyData(pubKey);
	setBadKeyData(privKey);
	
	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
		CSSM_ALGID_DSA,
		keySize,
		NULL,					// Seed
		NULL,					// Salt
		NULL,					// StartDate
		NULL,					// EndDate
		paramData,
		&ccHand);
	if(crtn) {
		printError("CSSM_CSP_CreateKeyGenContext", crtn);
		ocrtn = crtn;
		goto abort;
	}
	
	/* cook up attribute bits */
	if(pubIsRef) {
		pubAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
	}
	else {
		pubAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	}
	if(privIsRef) {
		privAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
	}
	else {
		privAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	}

	if(genParams) {
		/* 
		 * extra step - generate params - this just adds some
		 * info to the context
		 */
		CSSM_DATA dummy = {0, NULL};
		crtn = CSSM_GenerateAlgorithmParams(ccHand, 
			keySize, &dummy);
		if(crtn) {
			printError("CSSM_GenerateAlgorithmParams", crtn);
			return crtn;
		}
		appFreeCssmData(&dummy, CSSM_FALSE);
	}
	
	/* optional format specifiers */
	if(!pubIsRef && (pubFormat != CSSM_KEYBLOB_RAW_FORMAT_NONE)) {
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
			sizeof(uint32),	
			CAT_Uint32,
			NULL,
			pubFormat);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT)", crtn);
			return crtn;
		}
	}
	if(!privIsRef && (privFormat != CSSM_KEYBLOB_RAW_FORMAT_NONE)) {
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT,
			sizeof(uint32),			// currently sizeof CSSM_DATA
			CAT_Uint32,
			NULL,
			privFormat);
		if(crtn) {
			printError("AddContextAttribute(CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT)", crtn);
			return crtn;
		}
	}

	crtn = CSSM_GenerateKeyPair(ccHand,
		pubKeyUsage,
		pubAttr,
		&keyLabelData,
		pubKey,
		privKeyUsage,
		privAttr,
		&keyLabelData,			// same labels
		NULL,					// CredAndAclEntry
		privKey);
	if(crtn) {
		printError("CSSM_GenerateKeyPair", crtn);
		ocrtn = crtn;
		goto abort;
	}
	/* basic checks...*/
	if(privIsRef) {
		if(privKey->KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE) {
			printf("privKey blob type: exp %u got %u\n",
				CSSM_KEYBLOB_REFERENCE, (unsigned)privKey->KeyHeader.BlobType);
			ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
			goto abort;
		}
	}
	else {
		switch(privKey->KeyHeader.BlobType) {
			case CSSM_KEYBLOB_RAW:
				break;
			default:
				printf("privKey blob type: exp raw, got %u\n",
					(unsigned)privKey->KeyHeader.BlobType);
				ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
				goto abort;
		}
	}
	if(pubIsRef) {
		if(pubKey->KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE) {
			printf("pubKey blob type: exp %u got %u\n",
				CSSM_KEYBLOB_REFERENCE, (unsigned)pubKey->KeyHeader.BlobType);
			ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
			goto abort;
		}
	}
	else {
		switch(pubKey->KeyHeader.BlobType) {
			case CSSM_KEYBLOB_RAW:
				break;
			default:
				printf("pubKey blob type: exp raw or raw_berder, got %u\n",
					(unsigned)pubKey->KeyHeader.BlobType);
				ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
				goto abort;
		}
	}
abort:
	if(ccHand != 0) {
		crtn = CSSM_DeleteContext(ccHand);
		if(crtn) {
			printError("CSSM_DeleteContext", crtn);
			ocrtn = CSSM_ERRCODE_INTERNAL_ERROR;
		}
	}
	return ocrtn;
}


uint32 cspDefaultKeySize(uint32 alg)
{
	uint32 keySizeInBits;
	switch(alg) {
		case CSSM_ALGID_DES:
			keySizeInBits = CSP_DES_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_3DES_3KEY:
		case CSSM_ALGID_DESX:
			keySizeInBits = CSP_DES3_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_RC2:
			keySizeInBits = CSP_RC2_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_RC4:
			keySizeInBits = CSP_RC4_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_RC5:
			keySizeInBits = CSP_RC5_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_ASC:
			keySizeInBits = CSP_ASC_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_BLOWFISH:
			keySizeInBits = CSP_BFISH_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_CAST:
			keySizeInBits = CSP_CAST_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_IDEA:
			keySizeInBits = CSP_IDEA_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_AES:
			keySizeInBits = CSP_AES_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_SHA1HMAC:
			keySizeInBits = CSP_HMAC_SHA_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_MD5HMAC:
			keySizeInBits = CSP_HMAC_MD5_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_FEE:
			keySizeInBits = CSP_FEE_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_RSA:
			keySizeInBits = CSP_RSA_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_DSA:
			keySizeInBits = CSP_DSA_KEY_SIZE_DEFAULT;
			break;
		case CSSM_ALGID_NONE:
			keySizeInBits = CSP_NULL_CRYPT_KEY_SIZE_DEF;
			break;
		default:
			printf("***cspDefaultKeySize: Unknown symmetric algorithm\n");
			keySizeInBits = 0;
			break;
	}
	return keySizeInBits;
}

/*
 * Create a random symmetric key.
 */
CSSM_KEY_PTR cspGenSymKey(CSSM_CSP_HANDLE cspHand,
		uint32 				alg,
		const char 			*keyLabel,
		unsigned 			keyLabelLen,
		uint32 				keyUsage,		// CSSM_KEYUSE_ENCRYPT, etc.
		uint32 				keySizeInBits,
		CSSM_BOOL			refKey)
{
	CSSM_KEY_PTR 		symKey = (CSSM_KEY_PTR)CSSM_MALLOC(sizeof(CSSM_KEY));
	CSSM_RETURN			crtn;
	CSSM_CC_HANDLE 		ccHand;
	uint32				keyAttr;
	CSSM_DATA			dummyLabel;
	
	if(symKey == NULL) {
		printf("Insufficient heap space\n");
		return NULL;
	}
	memset(symKey, 0, sizeof(CSSM_KEY));
	setBadKeyData(symKey);
	if(keySizeInBits == CSP_KEY_SIZE_DEFAULT) {
		keySizeInBits = cspDefaultKeySize(alg);
	}
	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
		alg,
		keySizeInBits,	// keySizeInBits
		NULL,			// Seed
		NULL,			// Salt
		NULL,			// StartDate
		NULL,			// EndDate
		NULL,			// Params
		&ccHand);
	if(crtn) {
		printError("CSSM_CSP_CreateKeyGenContext", crtn);
		goto errorOut;
	}
	if(refKey) {
		keyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
	}
	else {
		keyAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	}
	dummyLabel.Length = keyLabelLen;
	dummyLabel.Data = (uint8 *)keyLabel;

	crtn = CSSM_GenerateKey(ccHand,
		keyUsage,
		keyAttr,
		&dummyLabel,
		NULL,			// ACL
		symKey);
	if(crtn) {
		printError("CSSM_GenerateKey", crtn);
		goto errorOut;
	}
	crtn = CSSM_DeleteContext(ccHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		goto errorOut;
	}
	return symKey;
errorOut:
	CSSM_FREE(symKey);
	return NULL;
}

/*
 * Derive symmetric key.
 * Note in the X CSP, we never return an IV. 
 */
CSSM_KEY_PTR cspDeriveKey(CSSM_CSP_HANDLE cspHand,
		uint32 				deriveAlg,		// CSSM_ALGID_PKCS5_PBKDF2, etc.
		uint32				keyAlg,			// CSSM_ALGID_RC5, etc.
		const char 			*keyLabel,
		unsigned 			keyLabelLen,
		uint32 				keyUsage,		// CSSM_KEYUSE_ENCRYPT, etc.
		uint32 				keySizeInBits,
		CSSM_BOOL			isRefKey,
		CSSM_DATA_PTR		password,		// in PKCS-5 lingo
		CSSM_DATA_PTR		salt,			// ditto
		uint32				iterationCnt,	// ditto
		CSSM_DATA_PTR		initVector)		// mallocd & RETURNED
{
	CSSM_KEY_PTR 				symKey = (CSSM_KEY_PTR)
									CSSM_MALLOC(sizeof(CSSM_KEY));
	CSSM_RETURN					crtn;
	CSSM_CC_HANDLE 				ccHand;
	uint32						keyAttr;
	CSSM_DATA					dummyLabel;
	CSSM_PKCS5_PBKDF2_PARAMS 	pbeParams;
	CSSM_DATA					pbeData;
	CSSM_ACCESS_CREDENTIALS		creds;
	
	if(symKey == NULL) {
		printf("Insufficient heap space\n");
		return NULL;
	}
	memset(symKey, 0, sizeof(CSSM_KEY));
	setBadKeyData(symKey);
	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	if(keySizeInBits == CSP_KEY_SIZE_DEFAULT) {
		keySizeInBits = cspDefaultKeySize(keyAlg);
	}
	crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
		deriveAlg,
		keyAlg,
		keySizeInBits,
		&creds,
		NULL,			// BaseKey
		iterationCnt,
		salt,
		NULL,			// seed
		&ccHand);
	if(crtn) {
		printError("CSSM_CSP_CreateDeriveKeyContext", crtn);
		goto errorOut;
	}
	keyAttr = CSSM_KEYATTR_EXTRACTABLE;
	if(isRefKey) {
		keyAttr |= (CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE);
	}
	else {
		keyAttr |= CSSM_KEYATTR_RETURN_DATA;
	}
	dummyLabel.Length = keyLabelLen;
	dummyLabel.Data = (uint8 *)keyLabel;
	
	/* passing in password is pretty strange....*/
	pbeParams.Passphrase = *password;
	pbeParams.PseudoRandomFunction = 
			CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1;
	pbeData.Data = (uint8 *)&pbeParams;
	pbeData.Length = sizeof(pbeParams);
	crtn = CSSM_DeriveKey(ccHand,
		&pbeData,
		keyUsage,
		keyAttr,
		&dummyLabel,
		NULL,			// cred and acl
		symKey);
	if(crtn) {
		printError("CSSM_DeriveKey", crtn);
		goto errorOut;
	}
	/* copy IV back to caller */
	/* Nope, not supported */
	#if 0
	if(pbeParams.InitVector.Data != NULL) {
		if(initVector->Data != NULL) {
			if(initVector->Length < pbeParams.InitVector.Length) {
				printf("***Insufficient InitVector\n");
				goto errorOut;
			}
		}
		else {
			initVector->Data = 
				(uint8 *)CSSM_MALLOC(pbeParams.InitVector.Length);
		}
		memmove(initVector->Data, pbeParams.InitVector.Data,
				pbeParams.InitVector.Length);
		initVector->Length = pbeParams.InitVector.Length;
		CSSM_FREE(pbeParams.InitVector.Data);
	}
	else {
		printf("***Warning: CSSM_DeriveKey, no InitVector\n");
	}
	#endif
	crtn = CSSM_DeleteContext(ccHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		goto errorOut;
	}
	return symKey;
errorOut:
	CSSM_FREE(symKey);
	return NULL;
}

/*
 * Cook up a symmetric key with specified key bits and other
 * params. Currently the CSPDL can only deal with reference keys except when
 * doing wrap/unwrap, so we manually cook up a raw key, then we null-unwrap it. 
 */
CSSM_RETURN cspGenSymKeyWithBits(
	CSSM_CSP_HANDLE		cspHand,
	CSSM_ALGORITHMS		keyAlg,
	CSSM_KEYUSE			keyUsage,
	const CSSM_DATA		*keyBits,
	unsigned			keySizeInBytes,
	CSSM_KEY_PTR		refKey)				// init'd and RETURNED
{
	CSSM_KEY			rawKey;
	CSSM_KEYHEADER_PTR	hdr = &rawKey.KeyHeader;
	CSSM_RETURN			crtn;
	
	/* set up a raw key the CSP will accept */
	memset(&rawKey, 0, sizeof(CSSM_KEY));
	hdr->HeaderVersion = CSSM_KEYHEADER_VERSION;
	hdr->BlobType = CSSM_KEYBLOB_RAW;
	hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
	hdr->AlgorithmId = keyAlg;
	hdr->KeyClass = CSSM_KEYCLASS_SESSION_KEY;
	hdr->LogicalKeySizeInBits = keySizeInBytes * 8;
	hdr->KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
	hdr->KeyUsage = keyUsage;
	appSetupCssmData(&rawKey.KeyData, keySizeInBytes);
	memmove(rawKey.KeyData.Data, keyBits->Data, keySizeInBytes);
	
	/* convert to a ref key */
	crtn = cspRawKeyToRef(cspHand, &rawKey, refKey);
	appFreeCssmData(&rawKey.KeyData, CSSM_FALSE);
	return crtn;
}

/*
 * Free a key. This frees a CSP's resources associated with the key if
 * the key is a reference key. It also frees key->KeyData. The CSSM_KEY
 * struct itself is not freed.
 * Note this has no effect on the CSP or DL cached keys unless the incoming
 * key is a reference key.
 */
CSSM_RETURN	cspFreeKey(CSSM_CSP_HANDLE cspHand,
	CSSM_KEY_PTR key)
{
	CSSM_RETURN crtn;
	crtn = CSSM_FreeKey(cspHand, 
		NULL,		// access cred
		key,
		CSSM_FALSE);	// delete - OK? maybe should parameterize?
	if(crtn) {
		printError("CSSM_FreeKey", crtn);
	}
	return crtn;
}

/* generate a random and reasonable key size in bits for specified CSSM algorithm */
uint32 randKeySizeBits(uint32 alg, 
	opType op)			// OT_Encrypt, etc.
{
	uint32 minSize;
	uint32 maxSize;
	uint32 size;
	
	switch(alg) {
		case CSSM_ALGID_DES:
			return CSP_DES_KEY_SIZE_DEFAULT;
		case CSSM_ALGID_3DES_3KEY:
		case CSSM_ALGID_DESX:
			return CSP_DES3_KEY_SIZE_DEFAULT;
		case CSSM_ALGID_ASC:
		case CSSM_ALGID_RC2:
		case CSSM_ALGID_RC4:
		case CSSM_ALGID_RC5:
			minSize = 5 * 8;
			maxSize = MAX_KEY_SIZE_RC245_BYTES * 8 ;	// somewhat arbitrary
			break;
		case CSSM_ALGID_BLOWFISH:
			minSize = 32;
			maxSize = 448;
			break;
		case CSSM_ALGID_CAST:
			minSize = 40;
			maxSize = 128;
			break;
		case CSSM_ALGID_IDEA:
			return CSP_IDEA_KEY_SIZE_DEFAULT;
		case CSSM_ALGID_RSA:
			minSize = CSP_RSA_KEY_SIZE_DEFAULT;
			maxSize = 1024;
			break;
		case CSSM_ALGID_DSA:
			/* signature only, no export restriction */
			minSize = 512;
			maxSize = 1024;
			break;
		case CSSM_ALGID_SHA1HMAC:
			minSize = 20 * 8;
			maxSize = 256 * 8;
			break;
		case CSSM_ALGID_MD5HMAC:
			minSize = 16 * 8;
			maxSize = 256 * 8;
			break;
		case CSSM_ALGID_FEE:
		case CSSM_ALGID_ECDSA:
		case CSSM_ALGID_SHA1WithECDSA:
			/* FEE, ECDSA require discrete sizes */
			size = genRand(1,3);
			switch(size) {
				case 1:
					return 31;
				case 2:
					if(alg == CSSM_ALGID_FEE) {
						return 127;
					}
					else {
						return 128;
					}
				case 3:
					return 161;
				case 5:
					return 192;
				default:
					printf("randKeySizeBits: internal error\n");
					return 0;
			}
		case CSSM_ALGID_AES:
			size = genRand(1, 3);
			switch(size) {
				case 1:
					return 128;
				case 2:
					return 192;
				case 3:
					return 256;
			}
		case CSSM_ALGID_NONE:
			return CSP_NULL_CRYPT_KEY_SIZE_DEF;
		default:
			printf("randKeySizeBits: unknown alg\n");
			return CSP_KEY_SIZE_DEFAULT;
	}
	size = genRand(minSize, maxSize);
	
	/* per-alg postprocessing.... */
	if(alg != CSSM_ALGID_RC2) {
		size &= ~0x7;
	}
	switch(alg) {
		case CSSM_ALGID_RSA:
			// new for X - strong keys */
			size &= ~(16 - 1);
			break;
		case CSSM_ALGID_DSA:
			/* size mod 64 == 0 */
			size &= ~(64 - 1);
			break;
		default:
			break;
	}
	return size;
}

#pragma mark --------- Encrypt/Decrypt ---------

/*
 * Encrypt/Decrypt
 */
/*
 * Common routine for encrypt/decrypt - cook up an appropriate context handle
 */
/*
 * When true, effectiveKeySizeInBits is passed down via the Params argument.
 * Otherwise, we add a customized context attribute.
 * Setting this true works with the stock Intel CSSM; this may well change.
 * Note this overloading prevent us from specifying RC5 rounds....
 */
#define EFFECTIVE_SIZE_VIA_PARAMS		0
CSSM_CC_HANDLE genCryptHandle(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEED, etc.
		uint32 mode,						// CSSM_ALGMODE_CBC, etc. - only for symmetric algs
		CSSM_PADDING padding,				// CSSM_PADDING_PKCS1, etc. 
		const CSSM_KEY *key0,
		const CSSM_KEY *key1,				// for CSSM_ALGID_FEED only - must be the 
											// public key
		const CSSM_DATA *iv,				// optional
		uint32 effectiveKeySizeInBits,		// 0 means skip this attribute
		uint32 rounds)						// ditto
{
	CSSM_CC_HANDLE cryptHand = 0;
	uint32 params;
	CSSM_RETURN crtn;
	CSSM_ACCESS_CREDENTIALS	creds;
	
	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	#if	EFFECTIVE_SIZE_VIA_PARAMS
	params = effectiveKeySizeInBits;
	#else
	params = 0;
	#endif
	switch(algorithm) {
		case CSSM_ALGID_DES:
		case CSSM_ALGID_3DES_3KEY_EDE:
		case CSSM_ALGID_DESX:
		case CSSM_ALGID_ASC:
		case CSSM_ALGID_RC2:
		case CSSM_ALGID_RC4:
		case CSSM_ALGID_RC5:
		case CSSM_ALGID_AES:
		case CSSM_ALGID_BLOWFISH:
		case CSSM_ALGID_CAST:
		case CSSM_ALGID_IDEA:
		case CSSM_ALGID_NONE:		// used for wrapKey()
			crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
				algorithm,
				mode,
				NULL,			// access cred
				key0,
				iv,				// InitVector
				padding,	
				NULL,			// Params
				&cryptHand);
			if(crtn) {
				printError("CSSM_CSP_CreateSymmetricContext", crtn);
				return 0;
			}
			break;
		case CSSM_ALGID_FEED:
		case CSSM_ALGID_FEEDEXP:
		case CSSM_ALGID_FEECFILE:
		case CSSM_ALGID_RSA:
			 crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
				algorithm,
				&creds,			// access
				key0,
				padding,
				&cryptHand);
			if(crtn) {
				printError("CSSM_CSP_CreateAsymmetricContext", crtn);
				return 0;
			}
			if(key1 != NULL) {
				/*
				 * FEED, some CFILE. Add (non-standard) second key attribute.
				 */
				crtn = AddContextAttribute(cryptHand,
						CSSM_ATTRIBUTE_PUBLIC_KEY,
						sizeof(CSSM_KEY),			// currently sizeof CSSM_DATA
						CAT_Ptr,
						key1,
						0);
				if(crtn) {
					printError("AddContextAttribute", crtn);
					return 0;
				}
			}
			if(mode != CSSM_ALGMODE_NONE) {
				/* special case, e.g., CSSM_ALGMODE_PUBLIC_KEY */
				crtn = AddContextAttribute(cryptHand,
						CSSM_ATTRIBUTE_MODE,
						sizeof(uint32),
						CAT_Uint32,
						NULL,
						mode);
				if(crtn) {
					printError("AddContextAttribute", crtn);
					return 0;
				}
			}
			break;
		default:
			printf("genCryptHandle: bogus algorithm\n");
			return 0;
	}
	#if		!EFFECTIVE_SIZE_VIA_PARAMS
	/* add optional EffectiveKeySizeInBits and rounds attributes */
	if(effectiveKeySizeInBits != 0) {
		CSSM_CONTEXT_ATTRIBUTE attr;
		attr.AttributeType = CSSM_ATTRIBUTE_EFFECTIVE_BITS;
		attr.AttributeLength = sizeof(uint32);
		attr.Attribute.Uint32 = effectiveKeySizeInBits;
		crtn = CSSM_UpdateContextAttributes(
			cryptHand,
			1,
			&attr);
		if(crtn) {
			printError("CSSM_UpdateContextAttributes", crtn);
			return crtn;
		}
	}
	#endif
	
	if(rounds != 0) {
		CSSM_CONTEXT_ATTRIBUTE attr;
		attr.AttributeType = CSSM_ATTRIBUTE_ROUNDS;
		attr.AttributeLength = sizeof(uint32);
		attr.Attribute.Uint32 = rounds;
		crtn = CSSM_UpdateContextAttributes(
			cryptHand,
			1,
			&attr);
		if(crtn) {
			printError("CSSM_UpdateContextAttributes", crtn);
			return crtn;
		}
	}

	return cryptHand;
}

CSSM_RETURN cspEncrypt(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEED, etc.
		uint32 mode,						// CSSM_ALGMODE_CBC, etc. - only for symmetric algs
		CSSM_PADDING padding,				// CSSM_PADDING_PKCS1, etc. 
		const CSSM_KEY *key,				// public or session key
		const CSSM_KEY *pubKey,				// for CSSM_ALGID_FEED, CSSM_ALGID_FEECFILE only
		uint32 effectiveKeySizeInBits,		// 0 means skip this attribute
		uint32 rounds,						// ditto
		const CSSM_DATA *iv,				// init vector, optional
		const CSSM_DATA *ptext,
		CSSM_DATA_PTR ctext,				// RETURNED
		CSSM_BOOL mallocCtext)				// if true, and ctext empty, malloc
											// by getting size from CSP
{
	CSSM_CC_HANDLE 	cryptHand;
	CSSM_RETURN		crtn;
	CSSM_SIZE		bytesEncrypted;
	CSSM_DATA		remData = {0, NULL};
	CSSM_RETURN		ocrtn = CSSM_OK;
	unsigned		origCtextLen;			// the amount we malloc, if any
	CSSM_RETURN		savedErr = CSSM_OK;
	CSSM_BOOL		restoreErr = CSSM_FALSE;
	
	cryptHand = genCryptHandle(cspHand, 
		algorithm, 
		mode, 
		padding,
		key, 
		pubKey, 
		iv, 
		effectiveKeySizeInBits,
		rounds);
	if(cryptHand == 0) {
		return CSSMERR_CSSM_INTERNAL_ERROR;
	}
	if(mallocCtext && (ctext->Length == 0)) {
		CSSM_QUERY_SIZE_DATA querySize;
		querySize.SizeInputBlock = ptext->Length;
		crtn = CSSM_QuerySize(cryptHand,
			CSSM_TRUE,						// encrypt
			1,
			&querySize);
		if(crtn) {
			printError("CSSM_QuerySize", crtn);
			ocrtn = crtn;
			goto abort;
		}
		if(querySize.SizeOutputBlock == 0) {
			/* CSP couldn't figure this out; skip our malloc */
			printf("***cspEncrypt: warning: cipherTextSize unknown; "
				"skipping malloc\n");
			origCtextLen = 0;
		}
		else {
			ctext->Data = (uint8 *)
				appMalloc(querySize.SizeOutputBlock, NULL);
			if(ctext->Data == NULL) {
				printf("Insufficient heap space\n");
				ocrtn = CSSM_ERRCODE_MEMORY_ERROR;
				goto abort;
			}
			ctext->Length = origCtextLen = querySize.SizeOutputBlock;
			memset(ctext->Data, 0, ctext->Length);
		}
	}
	else {
		origCtextLen = ctext->Length;
	}
	crtn = CSSM_EncryptData(cryptHand,
		ptext,
		1,
		ctext,
		1,
		&bytesEncrypted,
		&remData);
	if(crtn == CSSM_OK) {
		/*
		 * Deal with remData - its contents are included in bytesEncrypted.
		 */
		if((remData.Length != 0) && mallocCtext) {
			/* shouldn't happen - right? */
			if(bytesEncrypted > origCtextLen) {
				/* malloc and copy a new one */
				uint8 *newCdata = (uint8 *)appMalloc(bytesEncrypted, NULL);
				printf("**Warning: app malloced cipherBuf, but got nonzero "
					"remData!\n");
				if(newCdata == NULL) {
					printf("Insufficient heap space\n");
					ocrtn = CSSM_ERRCODE_MEMORY_ERROR;
					goto abort;
				}
				memmove(newCdata, ctext->Data, ctext->Length);
				memmove(newCdata+ctext->Length, remData.Data, remData.Length);
				CSSM_FREE(ctext->Data);
				ctext->Data = newCdata;
			}
			else {
				/* there's room left over */
				memmove(ctext->Data+ctext->Length, remData.Data, remData.Length);
			}
			ctext->Length = bytesEncrypted;
		}
		// NOTE: We return the proper length in ctext....
		ctext->Length = bytesEncrypted;
	}
	else {
		savedErr = crtn;
		restoreErr = CSSM_TRUE;
		printError("CSSM_EncryptData", crtn);
	}
abort:
	crtn = CSSM_DeleteContext(cryptHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	if(restoreErr) {
		ocrtn = savedErr;
	}
	return ocrtn;
}

#define PAD_IMPLIES_RAND_PTEXTSIZE	1
#define LOG_STAGED_OPS				0
#if		LOG_STAGED_OPS
#define soprintf(s)	printf s
#else
#define soprintf(s)
#endif

CSSM_RETURN cspStagedEncrypt(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEED, etc.
		uint32 mode,						// CSSM_ALGMODE_CBC, etc. - only for symmetric algs
		CSSM_PADDING padding,				// CSSM_PADDING_PKCS1, etc. 
		const CSSM_KEY *key,				// public or session key
		const CSSM_KEY *pubKey,				// for CSSM_ALGID_FEED, CSSM_ALGID_FEECFILE only
		uint32 effectiveKeySizeInBits,		// 0 means skip this attribute
		uint32 cipherBlockSize,				// ditto
		uint32 rounds,						// ditto
		const CSSM_DATA *iv,				// init vector, optional
		const CSSM_DATA *ptext,
		CSSM_DATA_PTR ctext,				// RETURNED, we malloc
		CSSM_BOOL multiUpdates)				// false:single update, true:multi updates
{
	CSSM_CC_HANDLE 	cryptHand;
	CSSM_RETURN		crtn;
	CSSM_SIZE		bytesEncrypted;			// per update
	CSSM_SIZE		bytesEncryptedTotal = 0;
	CSSM_RETURN		ocrtn = CSSM_OK;		// 'our' crtn
	unsigned		toMove;					// remaining
	unsigned		thisMove;				// bytes to encrypt on this update
	CSSM_DATA		thisPtext;				// running ptr into ptext
	CSSM_DATA		ctextWork;				// per update, mallocd by CSP
	CSSM_QUERY_SIZE_DATA querySize;
	uint8			*origCtext;				// initial ctext->Data
	unsigned		origCtextLen;			// amount we mallocd
	CSSM_BOOL		restoreErr = CSSM_FALSE;
	CSSM_RETURN		savedErr = CSSM_OK;
	
	
	cryptHand = genCryptHandle(cspHand, 
		algorithm, 
		mode, 
		padding,
		key, 
		pubKey, 
		iv,
		effectiveKeySizeInBits,
		rounds);
	if(cryptHand == 0) {
		return CSSMERR_CSP_INTERNAL_ERROR;
	}
	if(cipherBlockSize) {
		crtn = AddContextAttribute(cryptHand,
			CSSM_ATTRIBUTE_BLOCK_SIZE,
			sizeof(uint32),
			CAT_Uint32,
			NULL,
			cipherBlockSize);
		if(crtn) {
			printError("CSSM_UpdateContextAttributes", crtn);
			goto abort;
		}
	}
	
	/* obtain total required ciphertext size and block size */
	querySize.SizeInputBlock = ptext->Length;
	crtn = CSSM_QuerySize(cryptHand,
		CSSM_TRUE,						// encrypt
		1,
		&querySize);
	if(crtn) {
		printError("CSSM_QuerySize(1)", crtn);
		ocrtn = CSSMERR_CSP_INTERNAL_ERROR;
		goto abort;
	}
	if(querySize.SizeOutputBlock == 0) {
		/* CSP couldn't figure this out; skip our malloc - caller is taking its
		 * chances */
		printf("***cspStagedEncrypt: warning: cipherTextSize unknown; aborting\n");
		ocrtn = CSSMERR_CSP_INTERNAL_ERROR;
		goto abort;
	}
	else {
		origCtextLen = querySize.SizeOutputBlock;
		if(algorithm == CSSM_ALGID_ASC) {
			/* ASC is weird - the more chunks we do, the bigger the
			 * resulting ctext...*/
			origCtextLen *= 2;
		}
		ctext->Length = origCtextLen;
		ctext->Data   = origCtext = (uint8 *)appMalloc(origCtextLen, NULL);
		if(ctext->Data == NULL) {
			printf("Insufficient heap space\n");
			ocrtn = CSSMERR_CSP_MEMORY_ERROR;
			goto abort;
		}
		memset(ctext->Data, 0, ctext->Length);
	}

	crtn = CSSM_EncryptDataInit(cryptHand);
	if(crtn) {
		printError("CSSM_EncryptDataInit", crtn);
		ocrtn = crtn;
		goto abort;
	}
	
	toMove = ptext->Length;
	thisPtext.Data = ptext->Data;
	while(toMove) {
		if(multiUpdates) {
			thisMove = genRand(1, toMove);
		}
		else {
			/* just do one pass thru this loop */
			thisMove = toMove;
		}
		thisPtext.Length = thisMove;
		/* let CSP do the individual mallocs */
		ctextWork.Data = NULL;
		ctextWork.Length = 0;
		soprintf(("*** EncryptDataUpdate: ptextLen 0x%x\n", thisMove));
		crtn = CSSM_EncryptDataUpdate(cryptHand,
			&thisPtext,
			1,
			&ctextWork,
			1,
			&bytesEncrypted);
		if(crtn) {
			printError("CSSM_EncryptDataUpdate", crtn);
			ocrtn = crtn;
			goto abort;
		}
		// NOTE: We return the proper length in ctext....
		ctextWork.Length = bytesEncrypted;
		soprintf(("*** EncryptDataUpdate: ptextLen 0x%x  bytesEncrypted 0x%x\n",
			thisMove, bytesEncrypted));
		thisPtext.Data += thisMove;
		toMove         -= thisMove;
		if(bytesEncrypted > ctext->Length) {
			printf("cspStagedEncrypt: ctext overflow!\n");
			ocrtn = crtn;
			goto abort;
		}
		if(bytesEncrypted != 0) {
			memmove(ctext->Data, ctextWork.Data, bytesEncrypted);
			bytesEncryptedTotal += bytesEncrypted;
			ctext->Data         += bytesEncrypted;
			ctext->Length       -= bytesEncrypted;
		}
		if(ctextWork.Data != NULL) {
			CSSM_FREE(ctextWork.Data);
		}
	}
	/* OK, one more */
	ctextWork.Data = NULL;
	ctextWork.Length = 0;
	crtn = CSSM_EncryptDataFinal(cryptHand, &ctextWork);
	if(crtn) {
		printError("CSSM_EncryptDataFinal", crtn);
		savedErr = crtn;
		restoreErr = CSSM_TRUE;
		goto abort;
	}
	if(ctextWork.Length != 0) {
		bytesEncryptedTotal += ctextWork.Length;
		if(ctextWork.Length > ctext->Length) {
			printf("cspStagedEncrypt: ctext overflow (2)!\n");
			ocrtn = CSSMERR_CSP_INTERNAL_ERROR;
			goto abort;
		}
		memmove(ctext->Data, ctextWork.Data, ctextWork.Length);
	}
	if(ctextWork.Data) {
		/* this could have gotten mallocd and Length still be zero */
		CSSM_FREE(ctextWork.Data);
	}

	/* retweeze ctext */
	ctext->Data   = origCtext;
	ctext->Length = bytesEncryptedTotal;
abort:
	crtn = CSSM_DeleteContext(cryptHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	if(restoreErr) {
		/* give caller the error from the encrypt */
		ocrtn = savedErr;
	}
	return ocrtn;
}

CSSM_RETURN cspDecrypt(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEED, etc.
		uint32 mode,						// CSSM_ALGMODE_CBC, etc. - only for symmetric algs
		CSSM_PADDING padding,				// CSSM_PADDING_PKCS1, etc. 
		const CSSM_KEY *key,				// public or session key
		const CSSM_KEY *pubKey,				// for CSSM_ALGID_FEED, CSSM_ALGID_FEECFILE only
		uint32 effectiveKeySizeInBits,		// 0 means skip this attribute
		uint32 rounds,						// ditto
		const CSSM_DATA *iv,				// init vector, optional
		const CSSM_DATA *ctext,
		CSSM_DATA_PTR ptext,				// RETURNED
		CSSM_BOOL mallocPtext)				// if true and ptext->Length = 0,
											//   we'll malloc
{
	CSSM_CC_HANDLE 	cryptHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	CSSM_SIZE		bytesDecrypted;
	CSSM_DATA		remData = {0, NULL};
	unsigned		origPtextLen;			// the amount we malloc, if any

	cryptHand = genCryptHandle(cspHand, 
		algorithm, 
		mode, 
		padding,
		key, 
		pubKey, 
		iv,
		effectiveKeySizeInBits,
		rounds);
	if(cryptHand == 0) {
		return CSSMERR_CSP_INTERNAL_ERROR;
	}
	if(mallocPtext && (ptext->Length == 0)) {
		CSSM_QUERY_SIZE_DATA querySize;
		querySize.SizeInputBlock = ctext->Length;
		crtn = CSSM_QuerySize(cryptHand,
			CSSM_FALSE,						// encrypt
			1,
			&querySize);
		if(crtn) {
			printError("CSSM_QuerySize", crtn);
			ocrtn = crtn;
			goto abort;
		}
		if(querySize.SizeOutputBlock == 0) {
			/* CSP couldn't figure this one out; skip our malloc */
			printf("***cspDecrypt: warning: plainTextSize unknown; "
				"skipping malloc\n");
			origPtextLen = 0;
		}
		else {
			ptext->Data = 
				(uint8 *)appMalloc(querySize.SizeOutputBlock, NULL);
			if(ptext->Data == NULL) {
				printf("Insufficient heap space\n");
				ocrtn = CSSMERR_CSP_MEMORY_ERROR;
				goto abort;
			}
			ptext->Length = origPtextLen = querySize.SizeOutputBlock;
			memset(ptext->Data, 0, ptext->Length);
		}
	}
	else {
		origPtextLen = ptext->Length;
	}
	crtn = CSSM_DecryptData(cryptHand,
		ctext,
		1,
		ptext,
		1,
		&bytesDecrypted,
		&remData);
	if(crtn == CSSM_OK) {
		/*
		 * Deal with remData - its contents are included in bytesDecrypted.
		 */
		if((remData.Length != 0) && mallocPtext) {
			/* shouldn't happen - right? */
			if(bytesDecrypted > origPtextLen) {
				/* malloc and copy a new one */
				uint8 *newPdata = (uint8 *)appMalloc(bytesDecrypted, NULL);
				printf("**Warning: app malloced ClearBuf, but got nonzero "
					"remData!\n");
				if(newPdata == NULL) {
					printf("Insufficient heap space\n");
					ocrtn = CSSMERR_CSP_MEMORY_ERROR;
					goto abort;
				}
				memmove(newPdata, ptext->Data, ptext->Length);
				memmove(newPdata + ptext->Length,
					remData.Data, remData.Length);
				CSSM_FREE(ptext->Data);
				ptext->Data = newPdata;
			}
			else {
				/* there's room left over */
				memmove(ptext->Data + ptext->Length,
					remData.Data, remData.Length);
			}
			ptext->Length = bytesDecrypted;
		}
		// NOTE: We return the proper length in ptext....
		ptext->Length = bytesDecrypted;
		
		// FIXME - sometimes get mallocd RemData here, but never any valid data
		// there...side effect of CSPFullPluginSession's buffer handling logic;
		// but will we ever actually see valid data in RemData? So far we never
		// have....
		if(remData.Data != NULL) {
			appFree(remData.Data, NULL);
		}
	}
	else {
		printError("CSSM_DecryptData", crtn);
		ocrtn = crtn;
	}
abort:
	crtn = CSSM_DeleteContext(cryptHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}

CSSM_RETURN cspStagedDecrypt(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEED, etc.
		uint32 mode,						// CSSM_ALGMODE_CBC, etc. - only for symmetric algs
		CSSM_PADDING padding,				// CSSM_PADDING_PKCS1, etc. 
		const CSSM_KEY *key,				// public or session key
		const CSSM_KEY *pubKey,				// for CSSM_ALGID_FEED, CSSM_ALGID_FEECFILE only
		uint32 effectiveKeySizeInBits,		// 0 means skip this attribute
		uint32 cipherBlockSize,				// ditto
		uint32 rounds,						// ditto
		const CSSM_DATA *iv,				// init vector, optional
		const CSSM_DATA *ctext,
		CSSM_DATA_PTR ptext,				// RETURNED, we malloc
		CSSM_BOOL multiUpdates)				// false:single update, true:multi updates
{
	CSSM_CC_HANDLE 	cryptHand;
	CSSM_RETURN		crtn;
	CSSM_SIZE		bytesDecrypted;			// per update
	CSSM_SIZE		bytesDecryptedTotal = 0;
	CSSM_RETURN		ocrtn = CSSM_OK;		// 'our' crtn
	unsigned		toMove;					// remaining
	unsigned		thisMove;				// bytes to encrypt on this update
	CSSM_DATA		thisCtext;				// running ptr into ptext
	CSSM_DATA		ptextWork;				// per update, mallocd by CSP
	CSSM_QUERY_SIZE_DATA querySize;
	uint8			*origPtext;				// initial ptext->Data
	unsigned		origPtextLen;			// amount we mallocd
	
	cryptHand = genCryptHandle(cspHand, 
		algorithm, 
		mode, 
		padding,
		key, 
		pubKey, 
		iv,
		effectiveKeySizeInBits,
		rounds);
	if(cryptHand == 0) {
		return CSSMERR_CSP_INTERNAL_ERROR;
	}
	if(cipherBlockSize) {
		crtn = AddContextAttribute(cryptHand,
			CSSM_ATTRIBUTE_BLOCK_SIZE,
			sizeof(uint32),
			CAT_Uint32,
			NULL,
			cipherBlockSize);
		if(crtn) {
			printError("CSSM_UpdateContextAttributes", crtn);
			goto abort;
		}
	}
	
	/* obtain total required ciphertext size and block size */
	querySize.SizeInputBlock = ctext->Length;
	crtn = CSSM_QuerySize(cryptHand,
		CSSM_FALSE,						// encrypt
		1,
		&querySize);
	if(crtn) {
		printError("CSSM_QuerySize(1)", crtn);
		ocrtn = crtn;
		goto abort;
	}
	
	/* required ptext size should be independent of number of chunks */
	if(querySize.SizeOutputBlock == 0) {
		printf("***warning: cspStagedDecrypt: plainTextSize unknown; aborting\n");
		ocrtn = CSSMERR_CSP_INTERNAL_ERROR;
		goto abort;
	}
	else {
		// until exit, ptext->Length indicates remaining bytes of usable data in
		// ptext->Data
		ptext->Length = origPtextLen = querySize.SizeOutputBlock;
		ptext->Data   = origPtext    = 
			(uint8 *)appMalloc(origPtextLen, NULL);
		if(ptext->Data == NULL) {
			printf("Insufficient heap space\n");
			ocrtn = CSSMERR_CSP_INTERNAL_ERROR;
			goto abort;
		}
		memset(ptext->Data, 0, ptext->Length);
	}
	
	crtn = CSSM_DecryptDataInit(cryptHand);
	if(crtn) {
		printError("CSSM_DecryptDataInit", crtn);
		ocrtn = crtn;
		goto abort;
	}
	toMove = ctext->Length;
	thisCtext.Data = ctext->Data;
	while(toMove) {
		if(multiUpdates) {
			thisMove = genRand(1, toMove);
		}
		else {
			/* just do one pass thru this loop */
			thisMove = toMove;
		}
		thisCtext.Length = thisMove;
		/* let CSP do the individual mallocs */
		ptextWork.Data = NULL;
		ptextWork.Length = 0;
		soprintf(("*** DecryptDataUpdate: ctextLen 0x%x\n", thisMove));
		crtn = CSSM_DecryptDataUpdate(cryptHand,
			&thisCtext,
			1,
			&ptextWork,
			1,
			&bytesDecrypted);
		if(crtn) {
			printError("CSSM_DecryptDataUpdate", crtn);
			ocrtn = crtn;
			goto abort;
		}
		//
		// NOTE: We return the proper length in ptext....
		ptextWork.Length = bytesDecrypted;
		thisCtext.Data += thisMove;
		toMove         -= thisMove;
		if(bytesDecrypted > ptext->Length) {
			printf("cspStagedDecrypt: ptext overflow!\n");
			ocrtn = CSSMERR_CSP_INTERNAL_ERROR;
			goto abort;
		}
		if(bytesDecrypted != 0) {
			memmove(ptext->Data, ptextWork.Data, bytesDecrypted);
			bytesDecryptedTotal += bytesDecrypted;
			ptext->Data         += bytesDecrypted;
			ptext->Length       -= bytesDecrypted;
		}
		if(ptextWork.Data != NULL) {
			CSSM_FREE(ptextWork.Data);
		}
	}
	/* OK, one more */
	ptextWork.Data = NULL;
	ptextWork.Length = 0;
	crtn = CSSM_DecryptDataFinal(cryptHand, &ptextWork);
	if(crtn) {
		printError("CSSM_DecryptDataFinal", crtn);
		ocrtn = crtn;
		goto abort;
	}
	if(ptextWork.Length != 0) {
		bytesDecryptedTotal += ptextWork.Length;
		if(ptextWork.Length > ptext->Length) {
			printf("cspStagedDecrypt: ptext overflow (2)!\n");
			ocrtn = CSSMERR_CSP_INTERNAL_ERROR;
			goto abort;
		}
		memmove(ptext->Data, ptextWork.Data, ptextWork.Length);
	}
	if(ptextWork.Data) {
		/* this could have gotten mallocd and Length still be zero */
		CSSM_FREE(ptextWork.Data);
	}
	
	/* retweeze ptext */
	ptext->Data   = origPtext;
	ptext->Length = bytesDecryptedTotal;
abort:
	crtn = CSSM_DeleteContext(cryptHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}

#pragma mark --------- sign/verify/MAC ---------

/*
 * Signature routines
 * This all-in-one sign op has a special case for RSA keys. If the requested
 * alg is MD5 or SHA1, we'll do a manual digest op followed by raw RSA sign. 
 * Likewise, if it's CSSM_ALGID_DSA, we'll do manual SHA1 digest followed by 
 * raw DSA sign.
 */

CSSM_RETURN cspSign(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEE_MD5, etc.
		CSSM_KEY_PTR key,					// private key
		const CSSM_DATA *text,
		CSSM_DATA_PTR sig)					// RETURNED
{
	CSSM_CC_HANDLE	sigHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	const CSSM_DATA	*ptext;
	CSSM_DATA		digest = {0, NULL};
	CSSM_ALGORITHMS	digestAlg = CSSM_ALGID_NONE;

	/* handle special cases for raw sign */
	switch(algorithm) {
		case CSSM_ALGID_SHA1:
			digestAlg = CSSM_ALGID_SHA1;
			algorithm = CSSM_ALGID_RSA;
			break;
		case CSSM_ALGID_MD5:
			digestAlg = CSSM_ALGID_MD5;
			algorithm = CSSM_ALGID_RSA;
			break;
		case CSSM_ALGID_DSA:
			digestAlg = CSSM_ALGID_SHA1;
			algorithm = CSSM_ALGID_DSA;
			break;
		default:
			break;
	}
	if(digestAlg != CSSM_ALGID_NONE) {
		crtn = cspDigest(cspHand,
			digestAlg,
			CSSM_FALSE,			// mallocDigest
			text,
			&digest);
		if(crtn) {
			return crtn;
		}	
		/* sign digest with raw RSA/DSA */
		ptext = &digest;
	}
	else {
		ptext = text;
	}
	crtn = CSSM_CSP_CreateSignatureContext(cspHand,
		algorithm,
		NULL,				// passPhrase
		key,
		&sigHand);
	if(crtn) {
		printError("CSSM_CSP_CreateSignatureContext (1)", crtn);
		return crtn;
	}
	crtn = CSSM_SignData(sigHand,
		ptext,
		1,
		digestAlg,
		sig);
	if(crtn) {
		printError("CSSM_SignData", crtn);
		ocrtn = crtn;
	}
	crtn = CSSM_DeleteContext(sigHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	if(digest.Data != NULL) {
		CSSM_FREE(digest.Data);
	}
	return ocrtn;
}

/*
 * Staged sign. Each update does a random number of bytes 'till through.
 */
CSSM_RETURN cspStagedSign(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEE_MD5, etc.
		CSSM_KEY_PTR key,					// private key
		const CSSM_DATA *text,
		CSSM_BOOL multiUpdates,				// false:single update, true:multi updates
		CSSM_DATA_PTR sig)					// RETURNED
{
	CSSM_CC_HANDLE	sigHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	unsigned		thisMove;				// this update
	unsigned		toMove;					// total to go
	CSSM_DATA		thisText;				// actaully passed to update
	crtn = CSSM_CSP_CreateSignatureContext(cspHand,
		algorithm,
		NULL,				// passPhrase
		key,
		&sigHand);
	if(crtn) {
		printError("CSSM_CSP_CreateSignatureContext (1)", crtn);
		return crtn;
	}
	crtn = CSSM_SignDataInit(sigHand);
	if(crtn) {
		printError("CSSM_SignDataInit", crtn);
		ocrtn = crtn;
		goto abort;
	}
	toMove = text->Length;
	thisText.Data = text->Data;
	while(toMove) {
		if(multiUpdates) {
			thisMove = genRand(1, toMove);
		}
		else {
			thisMove = toMove;
		}
		thisText.Length = thisMove;
		crtn = CSSM_SignDataUpdate(sigHand,
			&thisText,
			1);
		if(crtn) {
			printError("CSSM_SignDataUpdate", crtn);
			ocrtn = crtn;
			goto abort;
		}
		thisText.Data += thisMove;
		toMove -= thisMove;
	}
	crtn = CSSM_SignDataFinal(sigHand, sig);
	if(crtn) {
		printError("CSSM_SignDataFinal", crtn);
		ocrtn = crtn;
		goto abort;
	}
abort:
	crtn = CSSM_DeleteContext(sigHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}

/*
 * This all-in-one verify op has a special case for RSA keys. If the requested
 * alg is MD5 or SHA1, we'll do a manual digest op followed by raw RSA verify.
 * Likewise, if it's CSSM_ALGID_DSA, we'll do manual SHA1 digest followed by 
 * raw DSA sign.
 */ 
 
CSSM_RETURN cspSigVerify(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEE_MD5, etc.
		CSSM_KEY_PTR key,					// public key
		const CSSM_DATA *text,
		const CSSM_DATA *sig,
		CSSM_RETURN expectResult)			// expected result is verify failure
											// CSSM_OK - expect success
{
	CSSM_CC_HANDLE	sigHand;
	CSSM_RETURN		ocrtn = CSSM_OK;
	CSSM_RETURN		crtn;
	const CSSM_DATA	*ptext;
	CSSM_DATA		digest = {0, NULL};
	CSSM_ALGORITHMS	digestAlg = CSSM_ALGID_NONE;
	
	/* handle special cases for raw sign */
	switch(algorithm) {
		case CSSM_ALGID_SHA1:
			digestAlg = CSSM_ALGID_SHA1;
			algorithm = CSSM_ALGID_RSA;
			break;
		case CSSM_ALGID_MD5:
			digestAlg = CSSM_ALGID_MD5;
			algorithm = CSSM_ALGID_RSA;
			break;
		case CSSM_ALGID_DSA:
			digestAlg = CSSM_ALGID_SHA1;
			algorithm = CSSM_ALGID_DSA;
			break;
		default:
			break;
	}
	if(digestAlg != CSSM_ALGID_NONE) {
		crtn = cspDigest(cspHand,
			digestAlg,
			CSSM_FALSE,			// mallocDigest
			text,
			&digest);
		if(crtn) {
			return crtn;
		}	
		/* sign digest with raw RSA/DSA */
		ptext = &digest;
	}
	else {
		ptext = text;
	}
	crtn = CSSM_CSP_CreateSignatureContext(cspHand,
		algorithm,
		NULL,				// passPhrase
		key,
		&sigHand);
	if(crtn) {
		printError("CSSM_CSP_CreateSignatureContext (3)", crtn);
		return crtn;
	}
	
	crtn = CSSM_VerifyData(sigHand,
		ptext,
		1,
		digestAlg,
		sig);
	if(crtn != expectResult) {
		if(!crtn) {
			printf("Unexpected good Sig Verify\n");
		}
		else {
			printError("CSSM_VerifyData", crtn);
		}
		ocrtn = CSSMERR_CSSM_INTERNAL_ERROR;
	}
	crtn = CSSM_DeleteContext(sigHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	if(digest.Data != NULL) {
		CSSM_FREE(digest.Data);
	}
	return ocrtn;
}

/*
 * Staged verify. Each update does a random number of bytes 'till through.
 */
CSSM_RETURN cspStagedSigVerify(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEE_MD5, etc.
		CSSM_KEY_PTR key,					// private key
		const CSSM_DATA *text,
		const CSSM_DATA *sig,
		CSSM_BOOL multiUpdates,				// false:single update, true:multi updates
		CSSM_RETURN expectResult)			// expected result is verify failure
											// CSSM_TRUE - expect success
{
	CSSM_CC_HANDLE	sigHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	unsigned		thisMove;				// this update
	unsigned		toMove;					// total to go
	CSSM_DATA		thisText;				// actaully passed to update
	crtn = CSSM_CSP_CreateSignatureContext(cspHand,
		algorithm,
		NULL,				// passPhrase
		key,
		&sigHand);
	if(crtn) {
		printError("CSSM_CSP_CreateSignatureContext (4)", crtn);
		return crtn;
	}
	crtn = CSSM_VerifyDataInit(sigHand);
	if(crtn) {
		printError("CSSM_VerifyDataInit", crtn);
		ocrtn = crtn;
		goto abort;
	}
	toMove = text->Length;
	thisText.Data = text->Data;
	while(toMove) {
		if(multiUpdates) {
			thisMove = genRand(1, toMove);
		}
		else {
			thisMove = toMove;
		}
		thisText.Length = thisMove;
		crtn = CSSM_VerifyDataUpdate(sigHand,
			&thisText,
			1);
		if(crtn) {
			printError("CSSM_VerifyDataUpdate", crtn);
			ocrtn = crtn;
			goto abort;
		}
		thisText.Data += thisMove;
		toMove -= thisMove;
	}
	crtn = CSSM_VerifyDataFinal(sigHand, sig);
	if(crtn != expectResult) {
		if(crtn) {
			printError("CSSM_VerifyDataFinal", crtn);
		}
		else {
			printf("Unexpected good Staged Sig Verify\n");
		}
		ocrtn = CSSMERR_CSSM_INTERNAL_ERROR;
	}
abort:
	crtn = CSSM_DeleteContext(sigHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}

/*
 * MAC routines
 */
CSSM_RETURN cspGenMac(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEE_MD5, etc.
		CSSM_KEY_PTR key,					// session key
		const CSSM_DATA *text,
		CSSM_DATA_PTR mac)					// RETURNED
{
	CSSM_CC_HANDLE	macHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	crtn = CSSM_CSP_CreateMacContext(cspHand,
		algorithm,
		key,
		&macHand);
	if(crtn) {
		printError("CSSM_CSP_CreateMacContext (1)", crtn);
		return crtn;
	}
	crtn = CSSM_GenerateMac(macHand,
		text,
		1,
		mac);
	if(crtn) {
		printError("CSSM_GenerateMac", crtn);
		ocrtn = crtn;
	}
	crtn = CSSM_DeleteContext(macHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}

/*
 * Staged generate mac. 
 */
CSSM_RETURN cspStagedGenMac(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEE_MD5, etc.
		CSSM_KEY_PTR key,					// private key
		const CSSM_DATA *text,
		CSSM_BOOL mallocMac,				// if true and digest->Length = 0, we'll 
											//		malloc
		CSSM_BOOL multiUpdates,				// false:single update, true:multi updates
		CSSM_DATA_PTR mac)					// RETURNED
{
	CSSM_CC_HANDLE	macHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	unsigned		thisMove;				// this update
	unsigned		toMove;					// total to go
	CSSM_DATA		thisText;				// actaully passed to update
	
	crtn = CSSM_CSP_CreateMacContext(cspHand,
		algorithm,
		key,
		&macHand);
	if(crtn) {
		printError("CSSM_CSP_CreateMacContext (2)", crtn);
		return crtn;
	}

	if(mallocMac && (mac->Length == 0)) {
		/* malloc mac - ask CSP for size */
		CSSM_QUERY_SIZE_DATA	querySize = {0, 0};
		crtn = CSSM_QuerySize(macHand,
			CSSM_TRUE,						// encrypt
			1,
			&querySize);
		if(crtn) {
			printError("CSSM_QuerySize(mac)", crtn);
			ocrtn = crtn;
			goto abort;
		}
		if(querySize.SizeOutputBlock == 0) {
			printf("Unknown mac size\n");
			ocrtn = CSSMERR_CSSM_INTERNAL_ERROR;
			goto abort;
		}
		mac->Data = (uint8 *)appMalloc(querySize.SizeOutputBlock, NULL);
		if(mac->Data == NULL) {
			printf("malloc failure\n");
			ocrtn = CSSMERR_CSSM_MEMORY_ERROR;
			goto abort;
		}
		mac->Length = querySize.SizeOutputBlock;
	}

	crtn = CSSM_GenerateMacInit(macHand);
	if(crtn) {
		printError("CSSM_GenerateMacInit", crtn);
		ocrtn = crtn;
		goto abort;
	}
	toMove = text->Length;
	thisText.Data = text->Data;
	
	while(toMove) {
		if(multiUpdates) {
			thisMove = genRand(1, toMove);
		}
		else {
			thisMove = toMove;
		}
		thisText.Length = thisMove;
		crtn = CSSM_GenerateMacUpdate(macHand,
			&thisText,
			1);
		if(crtn) {
			printError("CSSM_GenerateMacUpdate", crtn);
			ocrtn = crtn;
			goto abort;
		}
		thisText.Data += thisMove;
		toMove -= thisMove;
	}
	crtn = CSSM_GenerateMacFinal(macHand, mac);
	if(crtn) {
		printError("CSSM_GenerateMacFinal", crtn);
		ocrtn = crtn;
		goto abort;
	}
abort:
	crtn = CSSM_DeleteContext(macHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}

CSSM_RETURN cspMacVerify(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEE_MD5, etc.
		CSSM_KEY_PTR key,					// public key
		const CSSM_DATA *text,
		const CSSM_DATA_PTR mac,
		CSSM_RETURN expectResult)			// expected result 
											// CSSM_OK - expect success
{
	CSSM_CC_HANDLE	macHand;
	CSSM_RETURN		ocrtn = CSSM_OK;
	CSSM_RETURN		crtn;
	crtn = CSSM_CSP_CreateMacContext(cspHand,
		algorithm,
		key,
		&macHand);
	if(crtn) {
		printError("CSSM_CSP_CreateMacContext (3)", crtn);
		return crtn;
	}
	crtn = CSSM_VerifyMac(macHand,
		text,
		1,
		mac);
	if(crtn != expectResult) {
		if(crtn) {
			printError("CSSM_VerifyMac", crtn);
		}
		else {
			printf("Unexpected good Mac Verify\n");
		}
		ocrtn = CSSMERR_CSSM_INTERNAL_ERROR;
	}
	crtn = CSSM_DeleteContext(macHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}

/*
 * Staged mac verify. Each update does a random number of bytes 'till through.
 */
CSSM_RETURN cspStagedMacVerify(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEE_MD5, etc.
		CSSM_KEY_PTR key,					// private key
		const CSSM_DATA *text,
		const CSSM_DATA_PTR mac,
		CSSM_BOOL multiUpdates,				// false:single update, true:multi updates
		CSSM_RETURN expectResult)			// expected result is verify failure
											// CSSM_OK - expect success
{
	CSSM_CC_HANDLE	macHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	unsigned		thisMove;				// this update
	unsigned		toMove;					// total to go
	CSSM_DATA		thisText;				// actaully passed to update

	crtn = CSSM_CSP_CreateMacContext(cspHand,
		algorithm,
		key,
		&macHand);
	if(crtn) {
		printError("CSSM_CSP_CreateMacContext (4)", crtn);
		return crtn;
	}
	crtn = CSSM_VerifyMacInit(macHand);
	if(crtn) {
		printError("CSSM_VerifyMacInit", crtn);
		ocrtn = crtn;
		goto abort;
	}
	toMove = text->Length;
	thisText.Data = text->Data;
	
	while(toMove) {
		if(multiUpdates) {
			thisMove = genRand(1, toMove);
		}
		else {
			thisMove = toMove;
		}
		thisText.Length = thisMove;
		crtn = CSSM_VerifyMacUpdate(macHand,
			&thisText,
			1);
		if(crtn) {
			printError("CSSM_VerifyMacUpdate", crtn);
			ocrtn = crtn;
			goto abort;
		}
		thisText.Data += thisMove;
		toMove -= thisMove;
	}
	crtn = CSSM_VerifyMacFinal(macHand, mac);
	if(crtn != expectResult) {
		if(crtn) {
			printError("CSSM_VerifyMacFinal", crtn);
		}
		else {
			printf("Unexpected good Staged Mac Verify\n");
		}
		ocrtn = CSSMERR_CSSM_INTERNAL_ERROR;
	}
abort:
	crtn = CSSM_DeleteContext(macHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}

#pragma mark --------- Digest ---------

/*
 * Digest functions
 */
CSSM_RETURN cspDigest(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_MD5, etc.
		CSSM_BOOL mallocDigest,				// if true and digest->Length = 0, we'll malloc
		const CSSM_DATA *text,
		CSSM_DATA_PTR digest)
{
	CSSM_CC_HANDLE	digestHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	
	crtn = CSSM_CSP_CreateDigestContext(cspHand,
		algorithm,
		&digestHand);
	if(crtn) {
		printError("CSSM_CSP_CreateDIgestContext (1)", crtn);
		return crtn;
	}
	if(mallocDigest && (digest->Length == 0)) {
		/* malloc digest - ask CSP for size */
		CSSM_QUERY_SIZE_DATA	querySize = {0, 0};
		crtn = CSSM_QuerySize(digestHand,
			CSSM_FALSE,						// encrypt
			1,
			&querySize);
		if(crtn) {
			printError("CSSM_QuerySize(3)", crtn);
			ocrtn = crtn;
			goto abort;
		}
		if(querySize.SizeOutputBlock == 0) {
			printf("Unknown digest size\n");
			ocrtn = CSSMERR_CSSM_INTERNAL_ERROR;
			goto abort;
		}
		digest->Data = (uint8 *)appMalloc(querySize.SizeOutputBlock, NULL);
		if(digest->Data == NULL) {
			printf("malloc failure\n");
			ocrtn = CSSMERR_CSSM_MEMORY_ERROR;
			goto abort;
		}
		digest->Length = querySize.SizeOutputBlock;
	}
	crtn = CSSM_DigestData(digestHand,
		text,
		1,
		digest);
	if(crtn) {
		printError("CSSM_DigestData", crtn);
		ocrtn = crtn;
	}
abort:
	crtn = CSSM_DeleteContext(digestHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}

CSSM_RETURN cspStagedDigest(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_MD5, etc.
		CSSM_BOOL mallocDigest,				// if true and digest->Length = 0, we'll 
											//		malloc
		CSSM_BOOL multiUpdates,				// false:single update, true:multi updates
		const CSSM_DATA *text,
		CSSM_DATA_PTR digest)
{
	CSSM_CC_HANDLE	digestHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	unsigned		thisMove;				// this update
	unsigned		toMove;					// total to go
	CSSM_DATA		thisText;				// actually passed to update
	
	crtn = CSSM_CSP_CreateDigestContext(cspHand,
		algorithm,
		&digestHand);
	if(crtn) {
		printError("CSSM_CSP_CreateDigestContext (2)", crtn);
		return crtn;
	}
	if(mallocDigest && (digest->Length == 0)) {
		/* malloc digest - ask CSP for size */
		CSSM_QUERY_SIZE_DATA	querySize = {0, 0};
		crtn = CSSM_QuerySize(digestHand,
			CSSM_FALSE,						// encrypt
			1,
			&querySize);
		if(crtn) {
			printError("CSSM_QuerySize(4)", crtn);
			ocrtn = crtn;
			goto abort;
		}
		if(querySize.SizeOutputBlock == 0) {
			printf("Unknown digest size\n");
			ocrtn = CSSMERR_CSSM_INTERNAL_ERROR;
			goto abort;
		}
		digest->Data = (uint8 *)appMalloc(querySize.SizeOutputBlock, NULL);
		if(digest->Data == NULL) {
			printf("malloc failure\n");
			ocrtn = CSSMERR_CSSM_MEMORY_ERROR;
			goto abort;
		}
		digest->Length = querySize.SizeOutputBlock;
	}
	crtn = CSSM_DigestDataInit(digestHand);
	if(crtn) {
		printError("CSSM_DigestDataInit", crtn);
		ocrtn = crtn;
		goto abort;
	}
	toMove = text->Length;
	thisText.Data = text->Data;
	while(toMove) {
		if(multiUpdates) {
			thisMove = genRand(1, toMove);
		}
		else {
			thisMove = toMove;
		}
		thisText.Length = thisMove;
		crtn = CSSM_DigestDataUpdate(digestHand,
			&thisText,
			1);
		if(crtn) {
			printError("CSSM_DigestDataUpdate", crtn);
			ocrtn = crtn;
			goto abort;
		}
		thisText.Data += thisMove;
		toMove -= thisMove;
	}
	crtn = CSSM_DigestDataFinal(digestHand, digest);
	if(crtn) {
		printError("CSSM_DigestDataFinal", crtn);
		ocrtn = crtn;
		goto abort;
	}
abort:
	crtn = CSSM_DeleteContext(digestHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}

#pragma mark --------- wrap/unwrap ---------

/* wrap key function. */
CSSM_RETURN cspWrapKey(CSSM_CSP_HANDLE cspHand,
	const CSSM_KEY			*unwrappedKey,	
	const CSSM_KEY			*wrappingKey,
	CSSM_ALGORITHMS			wrapAlg,
	CSSM_ENCRYPT_MODE		wrapMode,
	CSSM_KEYBLOB_FORMAT		wrapFormat,			// NONE, PKCS7, PKCS8
	CSSM_PADDING			wrapPad,
	CSSM_DATA_PTR			initVector,			// for some wrapping algs
	CSSM_DATA_PTR			descrData,			// optional 
	CSSM_KEY_PTR			wrappedKey)			// RETURNED
{
	CSSM_CC_HANDLE		ccHand;
	CSSM_RETURN			crtn;
	CSSM_ACCESS_CREDENTIALS	creds;
	
	memset(wrappedKey, 0, sizeof(CSSM_KEY));
	setBadKeyData(wrappedKey);
	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	/* special case for NULL wrap - no wrapping key */
	if((wrappingKey == NULL) ||
	   (wrappingKey->KeyHeader.KeyClass == CSSM_KEYCLASS_SESSION_KEY)) {
		crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
				wrapAlg,
				wrapMode,
				&creds,			// passPhrase,
				wrappingKey,
				initVector,
				wrapPad,		// Padding
				0,				// Params
				&ccHand);
	}
	else {
		crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
				wrapAlg,
				&creds,	
				wrappingKey,
				wrapPad,		// padding
				&ccHand);
		if(crtn) {
			printError("cspWrapKey/CreateContext", crtn);
			return crtn;
		}
		if(initVector) {
			/* manually add IV for CMS. The actual low-level encrypt doesn't
			 * use it (and must ignore it). */
			crtn = AddContextAttribute(ccHand,
				CSSM_ATTRIBUTE_INIT_VECTOR,
				sizeof(CSSM_DATA),
				CAT_Ptr,
				initVector,
				0);
			if(crtn) {
				printError("CSSM_UpdateContextAttributes", crtn);
				return crtn;
			}
		}
	}
	if(crtn) {
		printError("cspWrapKey/CreateContext", crtn);
		return crtn;
	}
	if(wrapFormat != CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) {
		/* only add this attribute if it's not the default */
		CSSM_CONTEXT_ATTRIBUTE attr;
		attr.AttributeType = CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT;
		attr.AttributeLength = sizeof(uint32);
		attr.Attribute.Uint32 = wrapFormat;
		crtn = CSSM_UpdateContextAttributes(
			ccHand,
			1,
			&attr);
		if(crtn) {
			printError("CSSM_UpdateContextAttributes", crtn);
			return crtn;
		}
	}
	crtn = CSSM_WrapKey(ccHand,
		&creds,
		unwrappedKey,
		descrData,			// DescriptiveData
		wrappedKey);
	if(crtn != CSSM_OK) {
		printError("CSSM_WrapKey", crtn);
	}
	if(CSSM_DeleteContext(ccHand)) {
		printf("CSSM_DeleteContext failure\n");
	}
	return crtn;
}

/* unwrap key function. */
CSSM_RETURN cspUnwrapKey(CSSM_CSP_HANDLE cspHand,
	const CSSM_KEY			*wrappedKey,
	const CSSM_KEY			*unwrappingKey,
	CSSM_ALGORITHMS			unwrapAlg,
	CSSM_ENCRYPT_MODE		unwrapMode,
	CSSM_PADDING 			unwrapPad,
	CSSM_DATA_PTR			initVector,			// for some wrapping algs
	CSSM_KEY_PTR			unwrappedKey,		// RETURNED
	CSSM_DATA_PTR			descrData,			// required
	const char 				*keyLabel,
	unsigned 				keyLabelLen)
{
	CSSM_CC_HANDLE		ccHand;
	CSSM_RETURN			crtn;
	CSSM_DATA			labelData;
	uint32				keyAttr;
	CSSM_ACCESS_CREDENTIALS	creds;
	
	memset(unwrappedKey, 0, sizeof(CSSM_KEY));
	setBadKeyData(unwrappedKey);
	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	if((unwrappingKey == NULL) ||
	   (unwrappingKey->KeyHeader.KeyClass == CSSM_KEYCLASS_SESSION_KEY)) {
		crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
				unwrapAlg,
				unwrapMode,
				&creds,
				unwrappingKey,
				initVector,
				unwrapPad,
				0,				// Params
				&ccHand);
	}
	else {
		crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
				unwrapAlg,
				&creds,			// passPhrase,
				unwrappingKey,
				unwrapPad,		// Padding
				&ccHand);
		if(crtn) {
			printError("cspUnwrapKey/CreateContext", crtn);
			return crtn;
		}
		if(initVector) {
			/* manually add IV for CMS. The actual low-level encrypt doesn't
			 * use it (and must ignore it). */
			crtn = AddContextAttribute(ccHand,
				CSSM_ATTRIBUTE_INIT_VECTOR,
				sizeof(CSSM_DATA),
				CAT_Ptr,
				initVector,
				0);
			if(crtn) {
				printError("CSSM_UpdateContextAttributes", crtn);
				return crtn;
			}
		}
	}
	if(crtn) {
		printError("cspUnwrapKey/CreateContext", crtn);
		return crtn;
	}
	labelData.Data = (uint8 *)keyLabel;
	labelData.Length = keyLabelLen;
	
	/*
	 * New keyAttr - clear some old bits, make sure we ask for ref key
	 */
	keyAttr = wrappedKey->KeyHeader.KeyAttr;
	keyAttr &= ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE);
	keyAttr |= CSSM_KEYATTR_RETURN_REF;
	crtn = CSSM_UnwrapKey(ccHand,
		NULL,				// PublicKey
		wrappedKey,
		wrappedKey->KeyHeader.KeyUsage,
		keyAttr,
		&labelData,
		NULL,				// CredAndAclEntry
		unwrappedKey,
		descrData);			// required
	if(crtn != CSSM_OK) {
		printError("CSSM_UnwrapKey", crtn);
	}
	if(CSSM_DeleteContext(ccHand)) {
		printf("CSSM_DeleteContext failure\n");
	}
	return crtn;
}

/*
 * Simple NULL wrap to convert a reference key to a raw key.
 */
CSSM_RETURN cspRefKeyToRaw(
	CSSM_CSP_HANDLE cspHand,
	const CSSM_KEY *refKey,
	CSSM_KEY_PTR rawKey)		// init'd and RETURNED
{
	CSSM_DATA descData = {0, 0};
	
	memset(rawKey, 0, sizeof(CSSM_KEY));
	return cspWrapKey(cspHand,
		refKey,
		NULL,					// unwrappingKey
		CSSM_ALGID_NONE,
		CSSM_ALGMODE_NONE,
		CSSM_KEYBLOB_WRAPPED_FORMAT_NONE,
		CSSM_PADDING_NONE,
		NULL,					// IV
		&descData,
		rawKey);
}

/* unwrap raw key --> ref */
CSSM_RETURN cspRawKeyToRef(
	CSSM_CSP_HANDLE cspHand,
	const CSSM_KEY *rawKey,
	CSSM_KEY_PTR refKey)				// init'd and RETURNED
{
	CSSM_DATA descData = {0, 0};

	memset(refKey, 0, sizeof(CSSM_KEY));
	return cspUnwrapKey(cspHand,
		rawKey,
		NULL,		// unwrappingKey
		CSSM_ALGID_NONE,
		CSSM_ALGMODE_NONE,
		CSSM_PADDING_NONE,
		NULL,		// init vector
		refKey,
		&descData,
		"noLabel",
		7);
}


#pragma mark --------- FEE key/curve support ---------

/*
 * Generate random key size, primeType, curveType for FEE key for specified op.
 *
 * First just enumerate the curves we know about, with ECDSA-INcapable first
 */
 
typedef struct {
	uint32	keySizeInBits;
	uint32 	primeType;				// CSSM_FEE_PRIME_TYPE_xxx
	uint32 	curveType;				// CSSM_FEE_CURVE_TYPE_xxx
} feeCurveParams;

#define FEE_PROTOTYPE_CURVES	0
#if 	FEE_PROTOTYPE_CURVES
/* obsolete as of 4/9/2001 */
static feeCurveParams feeCurves[] = {
	{	31,		CSSM_FEE_PRIME_TYPE_MERSENNE,	CSSM_FEE_CURVE_TYPE_MONTGOMERY },
	{	127,	CSSM_FEE_PRIME_TYPE_MERSENNE,	CSSM_FEE_CURVE_TYPE_MONTGOMERY },
	{	127,	CSSM_FEE_PRIME_TYPE_GENERAL,	CSSM_FEE_CURVE_TYPE_MONTGOMERY },
	#define NUM_NON_ECDSA_CURVES	3
	
	/* start of Weierstrass, IEEE P1363-capable curves */
	{	31,		CSSM_FEE_PRIME_TYPE_MERSENNE,	CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
	{	40,		CSSM_FEE_PRIME_TYPE_FEE,		CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
	{	127,	CSSM_FEE_PRIME_TYPE_MERSENNE,	CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
	{	160,	CSSM_FEE_PRIME_TYPE_FEE,		CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
	{	160,	CSSM_FEE_PRIME_TYPE_GENERAL,	CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
	{	192,	CSSM_FEE_PRIME_TYPE_FEE,		CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
};
#else	/* FEE_PROTOTYPE_CURVES */
static feeCurveParams feeCurves[] = {
	{	31,		CSSM_FEE_PRIME_TYPE_MERSENNE,	CSSM_FEE_CURVE_TYPE_MONTGOMERY },
	{	127,	CSSM_FEE_PRIME_TYPE_MERSENNE,	CSSM_FEE_CURVE_TYPE_MONTGOMERY },
	#define NUM_NON_ECDSA_CURVES	2
	
	/* start of Weierstrass, IEEE P1363-capable curves */
	{	31,		CSSM_FEE_PRIME_TYPE_MERSENNE,	CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
	{	128,	CSSM_FEE_PRIME_TYPE_FEE,		CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
	{	161,	CSSM_FEE_PRIME_TYPE_FEE,		CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
	{	161,	CSSM_FEE_PRIME_TYPE_GENERAL,	CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
	{	192,	CSSM_FEE_PRIME_TYPE_GENERAL,	CSSM_FEE_CURVE_TYPE_WEIERSTRASS },
};
#endif	/* FEE_PROTOTYPE_CURVES */
#define NUM_FEE_CURVES	(sizeof(feeCurves) / sizeof(feeCurveParams))

void randFeeKeyParams(
	CSSM_ALGORITHMS	alg,			// ALGID_FEED, CSSM_ALGID_FEE_MD5, etc.
	uint32			*keySizeInBits,	// RETURNED
	uint32 			*primeType,		// CSSM_FEE_PRIME_TYPE_xxx, RETURNED
	uint32 			*curveType)		// CSSM_FEE_CURVE_TYPE_xxx, RETURNED
{
	unsigned minParams;
	unsigned die;
	feeCurveParams *feeParams;
	
	switch(alg) {
		case CSSM_ALGID_SHA1WithECDSA:
			minParams = NUM_NON_ECDSA_CURVES;
			break;
		default:
			minParams = 0;
			break;
	}
	die = genRand(minParams, (NUM_FEE_CURVES - 1));
	feeParams = &feeCurves[die];
	*keySizeInBits = feeParams->keySizeInBits;
	*primeType = feeParams->primeType;
	*curveType = feeParams->curveType;
}

/*
 * Obtain strings for primeType and curveType.
 */
const char *primeTypeStr(uint32 primeType)
{
	const char *p;
	switch(primeType) {
		case CSSM_FEE_PRIME_TYPE_MERSENNE:
			p = "Mersenne";
			break;
		case CSSM_FEE_PRIME_TYPE_FEE:
			p = "FEE";
			break;
		case CSSM_FEE_PRIME_TYPE_GENERAL:
			p = "General";
			break;
		case CSSM_FEE_PRIME_TYPE_DEFAULT:
			p = "Default";
			break;
		default:
			p = "***UNKNOWN***";
			break;
	}
	return p;
}

const char *curveTypeStr(uint32 curveType)
{
	const char *c;
	switch(curveType) {
		case CSSM_FEE_CURVE_TYPE_DEFAULT:
			c = "Default";
			break;
		case CSSM_FEE_CURVE_TYPE_MONTGOMERY:
			c = "Montgomery";
			break;
		case CSSM_FEE_CURVE_TYPE_WEIERSTRASS:
			c = "Weierstrass";
			break;
		default:
			c = "***UNKNOWN***";
			break;
	}
	return c;
}

/*
 * Perform FEE Key exchange via CSSM_DeriveKey. 
 */
#if 0
/* Not implemented in OS X */
CSSM_RETURN cspFeeKeyExchange(CSSM_CSP_HANDLE cspHand,
	CSSM_KEY_PTR 	privKey,
	CSSM_KEY_PTR 	pubKey,
	CSSM_KEY_PTR 	derivedKey,		// mallocd by caller
	
	/* remaining fields apply to derivedKey */
	uint32 			keyAlg,
	const char 		*keyLabel,
	unsigned 		keyLabelLen,
	uint32 			keyUsage,		// CSSM_KEYUSE_ENCRYPT, etc.
	uint32 			keySizeInBits)
{
	CSSM_CC_HANDLE	dkHand;
	CSSM_RETURN 	crtn;
	CSSM_DATA		labelData;
	
	if(derivedKey == NULL) {
		printf("cspFeeKeyExchange: no derivedKey\n");
		return CSSMERR_CSSM_INTERNAL_ERROR;
	}
	if((pubKey == NULL) ||
	   (pubKey->KeyHeader.KeyClass != CSSM_KEYCLASS_PUBLIC_KEY) ||
	   (pubKey->KeyHeader.BlobType != CSSM_KEYBLOB_RAW)) {
	 	printf("cspFeeKeyExchange: bad pubKey\n");
	 	return CSSMERR_CSSM_INTERNAL_ERROR;
	}
	if((privKey == NULL) ||
	   (privKey->KeyHeader.KeyClass != CSSM_KEYCLASS_PRIVATE_KEY) ||
	   (privKey->KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE)) {
	 	printf("cspFeeKeyExchange: bad privKey\n");
	 	return CSSMERR_CSSM_INTERNAL_ERROR;
	}
	memset(derivedKey, 0, sizeof(CSSM_KEY));
	
	crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
		CSSM_ALGID_FEE_KEYEXCH,			// AlgorithmID
		keyAlg,							// alg of the derived key
		keySizeInBits,
		NULL,							// access creds
		// FIXME
		0,								// IterationCount
		NULL,							// Salt
		NULL,							// Seed
		NULL);							// PassPhrase
	if(dkHand == 0) {
		printError("CSSM_CSP_CreateDeriveKeyContext");
		return CSSM_FAIL;
	} 
	labelData.Length = keyLabelLen;
	labelData.Data = (uint8 *)keyLabel;
	crtn = CSSM_DeriveKey(dkHand,
		privKey,
		&pubKey->KeyData,		// Param - pub key blob
		keyUsage,
		CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE |
				  CSSM_KEYATTR_SENSITIVE,
		&labelData,
		derivedKey);
	
	/* FIXME - save/restore error */
	CSSM_DeleteContext(dkHand);
	if(crtn) {
		printError("CSSM_DeriveKey");
	}
	return crtn;
}
#endif

#pragma mark --------- Key/DL/DB support ---------

/*
 * Add a DL/DB handle to a crypto context.
 */
CSSM_RETURN cspAddDlDbToContext(
	CSSM_CC_HANDLE ccHand,
	CSSM_DL_HANDLE dlHand,
	CSSM_DB_HANDLE dbHand)
{
	CSSM_DL_DB_HANDLE dlDb = { dlHand, dbHand };
	return AddContextAttribute(ccHand, 
		CSSM_ATTRIBUTE_DL_DB_HANDLE,
		sizeof(CSSM_ATTRIBUTE_DL_DB_HANDLE),
		CAT_Ptr,
		&dlDb,
		0);
}
	
/* 
 * Common routine to do a basic DB lookup by label and key type.
 * Query is aborted prior to exit.
 */
static CSSM_DB_UNIQUE_RECORD_PTR dlLookup(
	CSSM_DL_DB_HANDLE	dlDbHand,
	const CSSM_DATA		*keyLabel,
	CT_KeyType 			keyType,
	CSSM_HANDLE 		*resultHand,			// RETURNED
	CSSM_DATA_PTR		theData,				// RETURED
	CSSM_DB_RECORDTYPE	*recordType)			// RETURNED
{
	CSSM_QUERY						query;
	CSSM_SELECTION_PREDICATE		predicate;
	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
	CSSM_RETURN						crtn;
	
	switch(keyType) {
		case CKT_Public:
			query.RecordType = *recordType = CSSM_DL_DB_RECORD_PUBLIC_KEY;
			break;
		case CKT_Private:
			query.RecordType = *recordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
			break;
		case CKT_Session:
			query.RecordType = *recordType = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
			break;
		default:
			printf("Hey bozo! Give me a valid key type!\n");
			return NULL;
	}
	query.Conjunctive = CSSM_DB_NONE;
	query.NumSelectionPredicates = 1;
	predicate.DbOperator = CSSM_DB_EQUAL;
	
	predicate.Attribute.Info.AttributeNameFormat = 
		CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
	predicate.Attribute.Info.Label.AttributeName = (char *) "Label";
	predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	/* hope this cast is OK */
	predicate.Attribute.Value = (CSSM_DATA_PTR)keyLabel;
	query.SelectionPredicate = &predicate;
	
	query.QueryLimits.TimeLimit = 0;	// FIXME - meaningful?
	query.QueryLimits.SizeLimit = 1;	// FIXME - meaningful?
	query.QueryFlags = CSSM_QUERY_RETURN_DATA;	// FIXME - used?
	
	crtn = CSSM_DL_DataGetFirst(dlDbHand,
		&query,
		resultHand,
		NULL,
		theData,
		&record);
	/* abort only on success */
	if(crtn == CSSM_OK) {
		crtn = CSSM_DL_DataAbortQuery(dlDbHand, *resultHand);
		if(crtn) {
			printError("CSSM_DL_AbortQuery", crtn);
			return NULL;
		}
	}
	return record;
}

/*
 * Look up a key by label and type.
 */
CSSM_KEY_PTR cspLookUpKeyByLabel(
	CSSM_DL_HANDLE dlHand, 
	CSSM_DB_HANDLE dbHand, 
	const CSSM_DATA *labelData, 
	CT_KeyType keyType)
{
	CSSM_DB_UNIQUE_RECORD_PTR	record;
	CSSM_HANDLE					resultHand;
	CSSM_DATA					theData;
	CSSM_KEY_PTR				key;
	CSSM_DB_RECORDTYPE 			recordType;
	CSSM_DL_DB_HANDLE			dlDbHand;
	
	dlDbHand.DLHandle = dlHand;
	dlDbHand.DBHandle = dbHand;
	
	theData.Length = 0;
	theData.Data = NULL;
	
	record = dlLookup(dlDbHand,
		labelData,
		keyType,
		&resultHand,
		&theData,
		&recordType);
	if(record == NULL) {
		//printf("cspLookUpKeyByLabel: key not found\n");
		return NULL;
	}
	key = (CSSM_KEY_PTR)theData.Data;
	CSSM_DL_FreeUniqueRecord(dlDbHand, record);
	return key;
}

/*
 * Delete and free a key 
 */
CSSM_RETURN cspDeleteKey(
	CSSM_CSP_HANDLE		cspHand,		// for free
	CSSM_DL_HANDLE		dlHand,			// for delete
	CSSM_DB_HANDLE		dbHand,			// ditto
	const CSSM_DATA 	*labelData, 
	CSSM_KEY_PTR		key)
{
	CSSM_DB_UNIQUE_RECORD_PTR	record;
	CSSM_HANDLE					resultHand;
	CT_KeyType					keyType;
	CSSM_RETURN					crtn = CSSM_OK;
	CSSM_DB_RECORDTYPE 			recordType;
	CSSM_DL_DB_HANDLE			dlDbHand;
	
	if(key->KeyHeader.KeyAttr & CSSM_KEYATTR_PERMANENT) {
		/* first do a lookup based in this key's fields */
		switch(key->KeyHeader.KeyClass) {
			case CSSM_KEYCLASS_PUBLIC_KEY:
				keyType = CKT_Public;
				break;
			case CSSM_KEYCLASS_PRIVATE_KEY:
				keyType = CKT_Private;
				break;
			case CSSM_KEYCLASS_SESSION_KEY:
				keyType = CKT_Session;
				break;
			default:
				printf("Hey bozo! Give me a valid key type!\n");
				return -1;
		}

		dlDbHand.DLHandle = dlHand;
		dlDbHand.DBHandle = dbHand;
		
		record = dlLookup(dlDbHand,
			labelData,
			keyType,
			&resultHand,
			NULL,			// don't want actual data
			&recordType);
		if(record == NULL) {
			printf("cspDeleteKey: key not found in DL\n");
			return CSSMERR_DL_RECORD_NOT_FOUND;
		}
		
		/* OK, nuke it */
		crtn = CSSM_DL_DataDelete(dlDbHand, record);
		if(crtn) {
			printError("CSSM_DL_DataDelete", crtn);
		}
		CSSM_DL_FreeUniqueRecord(dlDbHand, record);
	}
		
	/* CSSM_FreeKey() should fail due to the delete, but it will
	 * still free KeyData....
	 * FIXME - we should be able to do this in this one single call - right?
	 */
	CSSM_FreeKey(cspHand, NULL, key, CSSM_FALSE);

	return crtn;
}

/*
 * Given any key in either blob or reference format,
 * obtain the associated SHA-1 hash. 
 */
CSSM_RETURN cspKeyHash(
	CSSM_CSP_HANDLE		cspHand,	
	const CSSM_KEY_PTR	key,			/* public key */
	CSSM_DATA_PTR		*hashData)		/* hash mallocd and RETURNED here */
{
	CSSM_CC_HANDLE		ccHand;
	CSSM_RETURN			crtn;
	CSSM_DATA_PTR		dp;
	
	*hashData = NULL;
	
	/* validate input params */
	if((key == NULL) ||
	   (hashData == NULL)) {
	   	printf("cspKeyHash: bogus args\n");
		return CSSMERR_CSSM_INTERNAL_ERROR;				
	}
	
	/* cook up a context for a passthrough op */
	crtn = CSSM_CSP_CreatePassThroughContext(cspHand,
	 	key,
		&ccHand);
	if(ccHand == 0) {
		printError("CSSM_CSP_CreatePassThroughContext", crtn);
		return crtn;
	}
	
	/* now it's up to the CSP */
	crtn = CSSM_CSP_PassThrough(ccHand,
		CSSM_APPLECSP_KEYDIGEST,
		NULL,
		(void **)&dp);
	if(crtn) {
		printError("CSSM_CSP_PassThrough(PUBKEYHASH)", crtn);
	}
	else {
		*hashData = dp;
		crtn = CSSM_OK;
	}
	CSSM_DeleteContext(ccHand);
	return crtn;
}