bsafeUtils.c   [plain text]


/*
 * bsafeUtils.c - common routines for CDSA/BSAFE compatibility testing
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <security_bsafe/bsafe.h>
#include <security_bsafe/aglobal.h>
#include "bsafeUtils.h"
#include <Security/cssmerr.h>
#include "common.h"

/*
 * Convert between BSAFE ITEM and CSSM_DATA
 */
static inline void buItemToCssmData(
	const ITEM 		*item,
	CSSM_DATA_PTR	cdata)
{
	cdata->Data   = item->data;
	cdata->Length = item->len;
}

static inline void buCssmDataToItem(
	const CSSM_DATA		*cdata,
	ITEM 				*item)
{
	item->data = cdata->Data;
	item->len  = cdata->Length;
}

/*
 * BSafe's Chooser table - all we'll ever need.
 */
/*static*/ B_ALGORITHM_METHOD *BSAFE_ALGORITHM_CHOOSER[] = {
    // digests
    &AM_SHA,
    &AM_MD5,
	&AM_MD2,

    // organizational
    &AM_CBC_ENCRYPT,
    &AM_CBC_DECRYPT,
    &AM_ECB_ENCRYPT,
    &AM_ECB_DECRYPT,
    &AM_OFB_ENCRYPT,
    &AM_OFB_DECRYPT,

    // DES & variants
    &AM_DES_ENCRYPT,
    &AM_DES_DECRYPT,
    &AM_DESX_ENCRYPT,
    &AM_DESX_DECRYPT,
    &AM_DES_EDE_ENCRYPT,
    &AM_DES_EDE_DECRYPT,

    // RCn stuff
    &AM_RC2_CBC_ENCRYPT,
    &AM_RC2_CBC_DECRYPT,
    &AM_RC2_ENCRYPT,
    &AM_RC2_DECRYPT,
    &AM_RC4_ENCRYPT,
    &AM_RC4_DECRYPT,
    &AM_RC5_ENCRYPT,
    &AM_RC5_DECRYPT,
    &AM_RC5_CBC_ENCRYPT,
    &AM_RC5_CBC_DECRYPT,

    // RSA
    &AM_RSA_STRONG_KEY_GEN,
    &AM_RSA_KEY_GEN,
    &AM_RSA_CRT_ENCRYPT_BLIND,
    &AM_RSA_CRT_DECRYPT_BLIND,
    &AM_RSA_ENCRYPT,
    &AM_RSA_DECRYPT,

    // DSA
    &AM_DSA_PARAM_GEN,
    &AM_DSA_KEY_GEN,

    // signatures
    &AM_DSA_SIGN,
    &AM_DSA_VERIFY,

    // random number generation
    &AM_MD5_RANDOM,
    &AM_SHA_RANDOM,

    // sentinel
    (B_ALGORITHM_METHOD *)NULL_PTR
};

/* 
 * Convert a BSAFE return to a CSSM error and optionally print the error msg with 
 * the op in which the error occurred.
 */
static CSSM_RETURN buBsafeErrToCssm(
	int brtn, 
	const char *op)
{
	char *errStr = NULL;
	CSSM_RETURN crtn;
	
    switch (brtn) {
		case 0:
			return CSSM_OK;
		case BE_ALLOC:
			crtn = CSSMERR_CSSM_MEMORY_ERROR;
			errStr = "BE_ALLOC";
			break;
		case BE_SIGNATURE:
			crtn = CSSMERR_CSP_VERIFY_FAILED;
			errStr = "BE_SIGNATURE";
			break;
		case BE_OUTPUT_LEN:
			crtn = CSSMERR_CSP_OUTPUT_LENGTH_ERROR;
			errStr = "BE_OUTPUT_LEN";
			break;
		case BE_INPUT_LEN:
			crtn = CSSMERR_CSP_INPUT_LENGTH_ERROR;
			errStr = "BE_INPUT_LEN";
			break;
		case BE_EXPONENT_EVEN:
			crtn = CSSMERR_CSP_INVALID_KEY;
			errStr = "BE_EXPONENT_EVEN";
			break;
		case BE_EXPONENT_LEN:
			crtn = CSSMERR_CSP_INVALID_KEY;
			errStr = "BE_EXPONENT_LEN";
			break;
		case BE_EXPONENT_ONE:
			crtn = CSSMERR_CSP_INVALID_KEY;
			errStr = "BE_EXPONENT_ONE";
			break;
		case BE_DATA:
			crtn = CSSMERR_CSP_INVALID_DATA;
			errStr = "BE_DATA";
			break;
		case BE_INPUT_DATA:
			crtn = CSSMERR_CSP_INVALID_DATA;
			errStr = "BE_INPUT_DATA";
			break;
		case BE_WRONG_KEY_INFO:
			crtn = CSSMERR_CSP_INVALID_KEY;
			errStr = "BE_WRONG_KEY_INFO";
			break;
        default:
			//@@@ translate BSafe errors intelligently
 			crtn = CSSM_ERRCODE_INTERNAL_ERROR;
			errStr = "Other BSAFE error";
			break;
    }
	if(op != NULL) {
		printf("%s: BSAFE error %d (%s)\n", op, brtn, errStr);
	}
	return crtn;
}

/*
 * Non-thread-safe global random B_ALGORITHM_OBJ and a reusable init for it.
 */
static B_ALGORITHM_OBJ 	bsafeRng = NULL;
#define BSAFE_RANDSIZE	64

static B_ALGORITHM_OBJ buGetRng()
{
	int brtn;
	uint8 seed[BSAFE_RANDSIZE];
	
	if(bsafeRng != NULL) {
		return bsafeRng;
	}
	brtn = B_CreateAlgorithmObject(&bsafeRng);
	if(brtn) {
		buBsafeErrToCssm(brtn, "B_CreateAlgorithmObject(&bsafeRng)");
		return NULL;
	}
	brtn = B_SetAlgorithmInfo(bsafeRng, AI_X962Random_V0, NULL_PTR);
	if(brtn) {
		buBsafeErrToCssm(brtn, "B_SetAlgorithmInfo(bsafeRng)");
		return NULL;
	}
	brtn = B_RandomInit(bsafeRng, BSAFE_ALGORITHM_CHOOSER, NULL);
 	if(brtn) {
		buBsafeErrToCssm(brtn, "B_SetAlgorithmInfo(bsafeRng)");
		return NULL;
	}
	appGetRandomBytes(seed, BSAFE_RANDSIZE);
	brtn = B_RandomUpdate(bsafeRng, seed, BSAFE_RANDSIZE, NULL);
	if(brtn) {
		buBsafeErrToCssm(brtn, "B_RandomUpdate");
		return NULL;
	}
	return bsafeRng;
}

/*
 * Create a symmetric key.
 */
CSSM_RETURN  buGenSymKey(
	uint32			keySizeInBits,
	const CSSM_DATA	*keyData,
	BU_KEY			*key)			// RETURNED
{
	int				brtn;
	B_KEY_OBJ		bkey = NULL;
	ITEM			item;
	unsigned		keyBytes = (keySizeInBits + 7) / 8;
	
	if(keyBytes > keyData->Length) {
		/* note it's OK to give us too much key data */
		printf("***buGenSymKey: Insufficient keyData\n");
		return CSSM_ERRCODE_INTERNAL_ERROR;
	}

	/* create a BSAFE key */
	brtn = B_CreateKeyObject(&bkey);
	if(brtn) {
		return buBsafeErrToCssm(brtn, "B_CreateKeyObject");
	}
	
	/* assign data to the key */
	item.data = keyData->Data;
	item.len = keyBytes;
	brtn = B_SetKeyInfo(bkey, KI_Item, (POINTER)&item);
	if(brtn) {
		return buBsafeErrToCssm(brtn, "B_SetKeyInfo");
	}
	else {
		*key = bkey;
		return CSSM_OK;
	}
}

/*
 * Create asymmetric key pair.
 * FIXME - additional params (e.g. DSA params, RSA exponent)?
 */
CSSM_RETURN buGenKeyPair(
	uint32			keySizeInBits,
	CSSM_ALGORITHMS	keyAlg,			// CSSM_ALGID_{RSA,DSA}
	BU_KEY			*pubKey,		// RETURNED
	BU_KEY			*privKey)		// RETURNED
{
	int						brtn;
	B_KEY_OBJ				bPubkey = NULL;
	B_KEY_OBJ				bPrivkey = NULL;
	B_ALGORITHM_OBJ			keypairGen = NULL;
	char					*op = NULL;
	A_RSA_KEY_GEN_PARAMS	params;
	unsigned char 			exp[1] = { 3 };
    B_ALGORITHM_OBJ 		genDsaAlg = NULL;
    B_ALGORITHM_OBJ 		dsaResult = NULL;
	B_DSA_PARAM_GEN_PARAMS 	dsaParams;
	A_DSA_PARAMS 			*kParams = NULL;
	
	/* create algorithm object */
	brtn = B_CreateAlgorithmObject(&keypairGen);
	if(brtn) {
		return CSSMERR_CSSM_MEMORY_ERROR;
	}
	
	/* create two BSAFE keys */
	brtn = B_CreateKeyObject(&bPubkey);
	if(brtn) {
		op ="B_CreateKeyObject";
		goto abort;
	}
	brtn = B_CreateKeyObject(&bPrivkey);
	if(brtn) {
		op ="B_CreateKeyObject";
		goto abort;
	}
	switch(keyAlg) {
		case CSSM_ALGID_RSA:
		{
			/* set RSA-specific params */
			params.modulusBits = keySizeInBits;
			/* hack - parameterize? */
			params.publicExponent.data = exp;
			params.publicExponent.len = 1;
			brtn = B_SetAlgorithmInfo(keypairGen, AI_RSAKeyGen, 
				(POINTER)&params);
			if(brtn) {
				op ="B_SetAlgorithmInfo(AI_RSAKeyGen)";
			}
			break;
		}
		case CSSM_ALGID_DSA:	
		{
			/* jump through hoops generating parameters */
			brtn = B_CreateAlgorithmObject(&genDsaAlg);
			if(brtn) {
				op ="B_CreateAlgorithmObject";
				break;
			}
			dsaParams.primeBits = keySizeInBits;
        	brtn = B_SetAlgorithmInfo(genDsaAlg, AI_DSAParamGen, (POINTER)&dsaParams);
			if(brtn) {
				op = "B_SetAlgorithmInfo(AI_DSAParamGen)";
				break;
			}
			brtn = B_GenerateInit(genDsaAlg, BSAFE_ALGORITHM_CHOOSER, NULL);
			if(brtn) {
				op = "B_GenerateInit(AI_DSAParamGen)";
				break;
			}
        	brtn = B_CreateAlgorithmObject(&dsaResult);
			if(brtn) {
				op = "B_CreateAlgorithmObject";
				break;
			}
        	brtn = B_GenerateParameters(genDsaAlg, dsaResult, buGetRng(), NULL);
			if(brtn) {
				op = "B_GenerateParameters";
				break;
			}
			
			/* dsaResult now has the parameters, which we must extract and then
			 * apply to the keypairGen object. Cool, huh? */
			brtn = B_GetAlgorithmInfo((POINTER *)&kParams, dsaResult, AI_DSAKeyGen);
			if(brtn) {
				op = "B_GetAlgorithmInfo(AI_DSAKeyGen)";
				break;
			}
			brtn = B_SetAlgorithmInfo(keypairGen, AI_DSAKeyGen, (POINTER)kParams);
			if(brtn) {
				op ="B_SetAlgorithmInfo(AI_DSAKeyGen)";
			}
			break;
		}
		default:
			printf("buGenKeyPair: algorithm not supported\n");
			return CSSMERR_CSSM_FUNCTION_NOT_IMPLEMENTED;
	}
	if(brtn) {
		goto abort;
	}
	
	/* keypairGen all set to go. */
	brtn = B_GenerateInit(keypairGen, 
		BSAFE_ALGORITHM_CHOOSER,
		(A_SURRENDER_CTX *)NULL);
	if(brtn) {
		op = "B_GenerateInit";
		goto abort;
	}
	brtn = B_GenerateKeypair(keypairGen,
		bPubkey,
		bPrivkey,
		buGetRng(),
		NULL);
	if(brtn) {
		op = "B_GenerateInit";
	}
abort:
	B_DestroyAlgorithmObject(&keypairGen);
	B_DestroyAlgorithmObject(&genDsaAlg);
	B_DestroyAlgorithmObject(&dsaResult);
	if(brtn) {
		B_DestroyKeyObject(&bPubkey);
		B_DestroyKeyObject(&bPrivkey);
		return buBsafeErrToCssm(brtn, op);
	}
	else {
		*pubKey = bPubkey;
		*privKey = bPrivkey;
		return CSSM_OK;
	}
}

/*
 * Free a key created in buGenSymKey or buGenKeyPair
 */
CSSM_RETURN buFreeKey(
	BU_KEY			key)
{
	B_KEY_OBJ bkey = (B_KEY_OBJ)key;
	B_DestroyKeyObject(&bkey);
	return CSSM_OK;
}

/*
 * encrypt/decrypt
 */
CSSM_RETURN buEncryptDecrypt(
	BU_KEY				key,
	CSSM_BOOL			forEncrypt,
	CSSM_ALGORITHMS		encrAlg,
	CSSM_ENCRYPT_MODE	mode,				// CSSM_ALGMODE_CBC, etc.
	const CSSM_DATA		*iv,				//Êoptional per mode
	uint32				effectiveKeyBits,	// optional per key alg (actually just RC2)
											// for RSA, key size in bits
	uint32				rounds,				// optional, RC5 only
	const CSSM_DATA		*inData,
	CSSM_DATA_PTR		outData)			// mallocd and RETURNED
{
	B_ALGORITHM_OBJ		alg;
	int 				brtn;
	char				fbCipher = 1;
	uint32				blockSize = 0;
	unsigned			outBufLen;
	unsigned			bytesMoved;
	CSSM_RETURN			crtn;
	char				useIv;
	
	// these variables are used in the switch below and need to 
	// live until after setAlgorithm()
	ITEM	 			bsIv;
    B_BLK_CIPHER_W_FEEDBACK_PARAMS spec;
	A_RC5_PARAMS		rc5Params;
	A_RC2_PARAMS		rc2Params;
	
	brtn = B_CreateAlgorithmObject(&alg);
	if(brtn) {
		return buBsafeErrToCssm(brtn, "B_CreateAlgorithmObject");
	}
	
	/* per-alg setup */
	switch(encrAlg) {
		case CSSM_ALGID_RC4:
			/* the easy one */
			brtn = B_SetAlgorithmInfo(alg, AI_RC4, NULL);
			if(brtn) {
				crtn = buBsafeErrToCssm(brtn, "B_SetAlgorithmInfo");
				goto abort;
			}
			fbCipher = 0;
			break;
			
		case CSSM_ALGID_RSA:
			/* assume encrypt via publicm decrypt via private */
			if(forEncrypt) {
				brtn = B_SetAlgorithmInfo(alg, AI_PKCS_RSAPublic, NULL);
			}
			else {
				brtn = B_SetAlgorithmInfo(alg, AI_PKCS_RSAPrivate, NULL);
			}
			if(brtn) {
				crtn = buBsafeErrToCssm(brtn, "B_SetAlgorithmInfo(RSA)");
				goto abort;
			}
			blockSize = (effectiveKeyBits + 7) / 8;
			fbCipher = 0;
			break;
			
		/* common code using AI_FeebackCipher */
        case CSSM_ALGID_DES:
            spec.encryptionMethodName = (POINTER)"des";
			blockSize = 8;
            break;
        case CSSM_ALGID_DESX:
            spec.encryptionMethodName = (POINTER)"desx";
 			blockSize = 8;
			break;
        case CSSM_ALGID_3DES_3KEY_EDE:
            spec.encryptionMethodName = (POINTER)"des_ede";
			blockSize = 8;
            break;
        case CSSM_ALGID_RC5:
            spec.encryptionMethodName = (POINTER)"rc5";
			spec.encryptionParams = (POINTER)&rc5Params;
			rc5Params.version = 0x10;
			rc5Params.rounds = rounds;
			rc5Params.wordSizeInBits = 32;
			blockSize = 8;
            break;
        case CSSM_ALGID_RC2:
            spec.encryptionMethodName = (POINTER)"rc2";
			spec.encryptionParams = (POINTER)&rc2Params;
			rc2Params.effectiveKeyBits = effectiveKeyBits;
 			blockSize = 8;
           break;
		/* add other non-AI_FeebackCipher algorithms here */
		default:
			printf("buEncryptDecrypt: unknown algorithm\n");
			return CSSM_ERRCODE_INTERNAL_ERROR;
	}
	if(fbCipher) {
		useIv = 1;		// default, except for ECB
		switch(mode) {
			case CSSM_ALGMODE_CBCPadIV8:
				spec.feedbackMethodName = (POINTER)"cbc";
				spec.paddingMethodName = (POINTER)"pad";
				break;
			case CSSM_ALGMODE_CBC_IV8: 
				spec.feedbackMethodName = (POINTER)"cbc";
				spec.paddingMethodName = (POINTER)"nopad";
				break;
			case CSSM_ALGMODE_OFB_IV8: 
				spec.feedbackMethodName = (POINTER)"cbc";
				spec.paddingMethodName = (POINTER)"nopad";
				break;
			case CSSM_ALGMODE_ECB: 
				/* this does not seem to work yet - need info from 
				 * RSA. Specify block size as the feedbackParams (per manual)
				 * and get a memmove error trying to copy from address 8; specify
				 * an IV and get BSAFE error 524 (BE_INPUT_DATA) error on the
				 * EncryptInit.
				 */
				spec.feedbackMethodName = (POINTER)"ecb";
				spec.paddingMethodName = (POINTER)"nopad";
				//useIv = 0;
				//spec.feedbackParams = (POINTER)8;
				break;
			default:
				printf("buEncryptDecrypt: unknown mode\n");
				return CSSM_ERRCODE_INTERNAL_ERROR;
		}
		if(useIv && (iv != NULL)) {
			buCssmDataToItem(iv, &bsIv);
			spec.feedbackParams = (POINTER)&bsIv;
		}
		
		brtn = B_SetAlgorithmInfo(alg, AI_FeedbackCipher, (POINTER)&spec);
		if(brtn) {
			crtn = buBsafeErrToCssm(brtn, "B_SetAlgorithmInfo");
			goto abort;
		}
	}
	
	/*
	 * OK, one way or another we have an algorithm object. Set up
	 * output buffer.
	 */
	if(forEncrypt) {
		outBufLen = inData->Length + blockSize;
	}
	else {
		outBufLen = inData->Length;
	}
	outData->Length = 0;
	outData->Data = NULL;
	crtn = appSetupCssmData(outData, outBufLen);
	if(crtn) {
		goto abort;
	}
	if(forEncrypt) {
		brtn = B_EncryptInit(alg, 
			(B_KEY_OBJ)key,
			BSAFE_ALGORITHM_CHOOSER,
			(A_SURRENDER_CTX *)NULL);
		if(brtn) {
			crtn = buBsafeErrToCssm(brtn, "B_EncryptInit");
			goto abort;
		}
		brtn = B_EncryptUpdate(alg,
			outData->Data,
			&bytesMoved,
			outBufLen,
			inData->Data,
			inData->Length,
			buGetRng(),		// randAlg
			NULL);			// surrender
		if(brtn) {
			crtn = buBsafeErrToCssm(brtn, "B_EncryptInit");
			goto abort;
		}
		outData->Length = bytesMoved;
		brtn = B_EncryptFinal(alg,
			outData->Data + bytesMoved,
			&bytesMoved,
			outBufLen - outData->Length,
			buGetRng(),		// randAlg
			NULL);			// surrender
		if(brtn) {
			crtn = buBsafeErrToCssm(brtn, "B_EncryptFinal");
			goto abort;
		}
		outData->Length += bytesMoved;
		crtn = CSSM_OK;
	}
	else {
		brtn = B_DecryptInit(alg, 
			(B_KEY_OBJ)key,
			BSAFE_ALGORITHM_CHOOSER,
			(A_SURRENDER_CTX *)NULL);
		if(brtn) {
			crtn = buBsafeErrToCssm(brtn, "B_DecryptInit");
			goto abort;
		}
		brtn = B_DecryptUpdate(alg,
			outData->Data,
			&bytesMoved,
			outBufLen,
			inData->Data,
			inData->Length,
			NULL,			// randAlg
			NULL);			// surrender
		if(brtn) {
			crtn = buBsafeErrToCssm(brtn, "B_DecryptUpdate");
			goto abort;
		}
		outData->Length = bytesMoved;
		brtn = B_DecryptFinal(alg,
			outData->Data + bytesMoved,
			&bytesMoved,
			outBufLen - outData->Length,
			NULL,			// randAlg
			NULL);			// surrender
		if(brtn) {
			crtn = buBsafeErrToCssm(brtn, "B_DecryptFinal");
			goto abort;
		}
		outData->Length += bytesMoved;
		crtn = CSSM_OK;
	}
abort:
	B_DestroyAlgorithmObject(&alg);
	return crtn;
}

/* CSSM sig alg --> B_INFO_TYPE */
static CSSM_RETURN cssmSigAlgToInfoType(
	CSSM_ALGORITHMS cssmAlg,
	B_INFO_TYPE		*infoType)
{
	switch(cssmAlg) {
		case CSSM_ALGID_SHA1WithRSA:
			*infoType = AI_SHA1WithRSAEncryption;
			break;
		case CSSM_ALGID_MD5WithRSA:
			*infoType = AI_MD5WithRSAEncryption;
			break;
		case CSSM_ALGID_SHA1WithDSA:
			*infoType = AI_DSAWithSHA1;
			break;
		default:
			printf("cssmSigAlgToInfoType: unknown algorithm\n");
			return CSSMERR_CSSM_FUNCTION_NOT_IMPLEMENTED;
	}
	return CSSM_OK;
}

/*
 * Sign/verify
 */
CSSM_RETURN buSign(
	BU_KEY				key,
	CSSM_ALGORITHMS		sigAlg,
	const CSSM_DATA		*ptext,
	uint32				keySizeInBits,		// to set up sig
	CSSM_DATA_PTR		sig)				// mallocd and RETURNED
{
	B_ALGORITHM_OBJ		alg = NULL;
	int 				brtn;
	B_INFO_TYPE			infoType;
	CSSM_RETURN			crtn;
	unsigned			sigBytes;
	
	brtn = B_CreateAlgorithmObject(&alg);
	if(brtn) {
		return buBsafeErrToCssm(brtn, "B_CreateAlgorithmObject");
	}
	crtn = cssmSigAlgToInfoType(sigAlg, &infoType);
	if(crtn) {
		return crtn;
	}
	brtn = B_SetAlgorithmInfo(alg, infoType, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_SetAlgorithmInfo");
		goto abort;
	}
	brtn = B_SignInit(alg, (B_KEY_OBJ)key, BSAFE_ALGORITHM_CHOOSER, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_SignInit");
		goto abort;
	}
	brtn = B_SignUpdate(alg, ptext->Data, ptext->Length, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_SignUpdate");
		goto abort;
	}
	
	/* prepare for sig, size of key */
	sigBytes = (keySizeInBits + 7) / 8;
	sig->Data = (uint8 *)CSSM_MALLOC(sigBytes);
	sig->Length = sigBytes;
	
	brtn = B_SignFinal(alg, sig->Data, &sigBytes, sigBytes, buGetRng(), NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_SignFinal");
		goto abort;
	}
	sig->Length = sigBytes;
	crtn = CSSM_OK;
abort:
	B_DestroyAlgorithmObject(&alg);
	return crtn;
}

CSSM_RETURN buVerify(
	BU_KEY				key,
	CSSM_ALGORITHMS		sigAlg,
	const CSSM_DATA		*ptext,
	const CSSM_DATA		*sig)				// mallocd and RETURNED
{
	B_ALGORITHM_OBJ		alg = NULL;
	int 				brtn;
	B_INFO_TYPE			infoType;
	CSSM_RETURN			crtn;
	
	brtn = B_CreateAlgorithmObject(&alg);
	if(brtn) {
		return buBsafeErrToCssm(brtn, "B_CreateAlgorithmObject");
	}
	crtn = cssmSigAlgToInfoType(sigAlg, &infoType);
	if(crtn) {
		return crtn;
	}
	brtn = B_SetAlgorithmInfo(alg, infoType, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_SetAlgorithmInfo");
		goto abort;
	}
	brtn = B_VerifyInit(alg, (B_KEY_OBJ)key, BSAFE_ALGORITHM_CHOOSER, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_VerifyInit");
		goto abort;
	}
	brtn = B_VerifyUpdate(alg, ptext->Data, ptext->Length, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_VerifyUpdate");
		goto abort;
	}
	brtn = B_VerifyFinal(alg, sig->Data, sig->Length, buGetRng(), NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_VerifyFinal");
		goto abort;
	}
	crtn = CSSM_OK;
abort:
	B_DestroyAlgorithmObject(&alg);
	return crtn;
}

/* 
 * generate MAC either one update (updateSizes == NULL) or 
 * specified set of update sizes.
 */
#define MAX_MAC_SIZE	20

CSSM_RETURN buGenMac(
	BU_KEY				key,				// any key, any size
	CSSM_ALGORITHMS		macAlg,				// only CSSM_ALGID_SHA1HMAC for now
	const CSSM_DATA		*ptext,
	unsigned			*updateSizes,		// NULL --> random updates
											// else null-terminated list of sizes
	CSSM_DATA_PTR		mac)				// mallocd and RETURNED 
{
	B_ALGORITHM_OBJ		alg = NULL;
	int 				brtn;
	CSSM_RETURN			crtn;
	B_DIGEST_SPECIFIER	digestInfo;
	B_INFO_TYPE			infoType;
	unsigned			macBytes;
	
	brtn = B_CreateAlgorithmObject(&alg);
	if(brtn) {
		return buBsafeErrToCssm(brtn, "B_CreateAlgorithmObject");
	}
	switch(macAlg) {
		case CSSM_ALGID_SHA1HMAC:
		case CSSM_ALGID_SHA1HMAC_LEGACY:
			digestInfo.digestInfoType = AI_SHA1;
			infoType = AI_HMAC;
			break;
		default:
			printf("buGenMac: alg not supported\n");
			return CSSMERR_CSSM_FUNCTION_NOT_IMPLEMENTED;
	}
	digestInfo.digestInfoParams = NULL;
	brtn = B_SetAlgorithmInfo(alg, infoType, (POINTER)&digestInfo);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_SetAlgorithmInfo");
		goto abort;
	}
	brtn = B_DigestInit(alg, (B_KEY_OBJ)key, BSAFE_ALGORITHM_CHOOSER, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_DigestInit");
		goto abort;
	}
	if(updateSizes) {
		uint8 *currData = ptext->Data;
		while(*updateSizes) {
			brtn = B_DigestUpdate(alg, currData, *updateSizes, NULL);
			if(brtn) {
				crtn = buBsafeErrToCssm(brtn, "B_DigestUpdate");
				goto abort;
			}
			currData += *updateSizes;
			updateSizes++;
		}
	}
	else {
		/* one-shot */
		brtn = B_DigestUpdate(alg, ptext->Data, ptext->Length, NULL);
		if(brtn) {
			crtn = buBsafeErrToCssm(brtn, "B_DigestUpdate");
			goto abort;
		}
	}
	/* prepare for mac, magically gleaned max size */
	macBytes = MAX_MAC_SIZE;
	mac->Data = (uint8 *)CSSM_MALLOC(macBytes);
	mac->Length = macBytes;
	
	brtn = B_DigestFinal(alg, mac->Data, &macBytes, macBytes, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_DigestFinal");
		goto abort;
	}
	mac->Length = macBytes;
	crtn = CSSM_OK;
abort:
	B_DestroyAlgorithmObject(&alg);
	return crtn;

}

/* generate digest */
#define MAX_DIGEST_SIZE		20

CSSM_RETURN buGenDigest(
	CSSM_ALGORITHMS		macAlg,				// CSSM_ALGID_SHA1, etc. */
	const CSSM_DATA		*ptext,
	CSSM_DATA_PTR		digest)				// mallocd and RETURNED 
{
	B_ALGORITHM_OBJ		alg = NULL;
	int 				brtn;
	CSSM_RETURN			crtn;
	B_INFO_TYPE			infoType;
	unsigned			hashBytes;
	
	brtn = B_CreateAlgorithmObject(&alg);
	if(brtn) {
		return buBsafeErrToCssm(brtn, "B_CreateAlgorithmObject");
	}
	switch(macAlg) {
		case CSSM_ALGID_SHA1:
			infoType = AI_SHA1;
			break;
		case CSSM_ALGID_MD5:
			infoType = AI_MD5;
			break;
		case CSSM_ALGID_MD2:
			infoType = AI_MD2;
			break;
		default:
			printf("buGenDigest: alg not supported\n");
			return CSSMERR_CSSM_FUNCTION_NOT_IMPLEMENTED;
	}
	brtn = B_SetAlgorithmInfo(alg, infoType, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_SetAlgorithmInfo");
		goto abort;
	}
	brtn = B_DigestInit(alg, NULL, BSAFE_ALGORITHM_CHOOSER, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_DigestInit");
		goto abort;
	}
	brtn = B_DigestUpdate(alg, ptext->Data, ptext->Length, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_DigestUpdate");
		goto abort;
	}
	
	/* prepare for digest, magically gleaned max size */
	hashBytes = MAX_DIGEST_SIZE;
	digest->Data = (uint8 *)CSSM_MALLOC(hashBytes);
	digest->Length = hashBytes;
	
	brtn = B_DigestFinal(alg, digest->Data, &hashBytes, hashBytes, NULL);
	if(brtn) {
		crtn = buBsafeErrToCssm(brtn, "B_DigestFinal");
		goto abort;
	}
	digest->Length = hashBytes;
	crtn = CSSM_OK;
abort:
	B_DestroyAlgorithmObject(&alg);
	return crtn;

}

/*
 * Convert between BSAFE and CDSA private keys
 */
CSSM_RETURN buBsafePrivKeyToCdsa(
	CSSM_ALGORITHMS		keyAlg,
	uint32				keySizeInBits,
	BU_KEY				bsafePrivKey,
	CSSM_KEY_PTR		cdsaPrivKey)
{
	B_INFO_TYPE			infoType;
	ITEM				*keyBlob;
	int					brtn;
	CSSM_KEYBLOB_FORMAT	format;
	CSSM_KEYHEADER_PTR	hdr = &cdsaPrivKey->KeyHeader;
	
	/* what kind of info? */
	switch(keyAlg) {
		case CSSM_ALGID_RSA:
			infoType = KI_PKCS_RSAPrivateBER;
			format = CSSM_KEYBLOB_RAW_FORMAT_PKCS8;
			break;
		case CSSM_ALGID_DSA:
			infoType = KI_DSAPrivateBER;
			format = CSSM_KEYBLOB_RAW_FORMAT_FIPS186;
			break;
		default:
			printf("***buBsafePrivKeyToCdsa: bogus keyAlg\n");
			return CSSMERR_CSSM_FUNCTION_NOT_IMPLEMENTED;
	}
	
	/* get the blob */
	brtn = B_GetKeyInfo((POINTER *)&keyBlob,
		(B_KEY_OBJ)bsafePrivKey,
		infoType);
	if(brtn) {
		return buBsafeErrToCssm(brtn, "B_GetKeyInfo");
	}
	
	/* copy blob to CDSA key */
	cdsaPrivKey->KeyData.Data = (uint8 *)CSSM_MALLOC(keyBlob->len);
	cdsaPrivKey->KeyData.Length = keyBlob->len;
	memmove(cdsaPrivKey->KeyData.Data, keyBlob->data, keyBlob->len);
	
	/* set up CSSM key header */
	memset(hdr, 0, sizeof(CSSM_KEYHEADER));
	hdr->HeaderVersion = CSSM_KEYHEADER_VERSION;
	hdr->BlobType = CSSM_KEYBLOB_RAW;
	hdr->Format = format;
	hdr->AlgorithmId = keyAlg;
	hdr->KeyClass = CSSM_KEYCLASS_PRIVATE_KEY;
	hdr->LogicalKeySizeInBits = keySizeInBits;
	hdr->KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
	hdr->KeyUsage = CSSM_KEYUSE_ANY;
	return CSSM_OK;
}

CSSM_RETURN buCdsaPrivKeyToBsafe(
	CSSM_KEY_PTR		cdsaPrivKey,
	BU_KEY				*bsafePrivKey)
{
	int 		brtn;
	B_KEY_OBJ	privKey = NULL;
	ITEM		keyBlob;
	B_INFO_TYPE	infoType;
	
	/* what kind of info? */
	switch(cdsaPrivKey->KeyHeader.AlgorithmId) {
		case CSSM_ALGID_RSA:
			infoType = KI_PKCS_RSAPrivateBER;
			break;
		case CSSM_ALGID_DSA:
			infoType = KI_DSAPrivateBER;
			break;
		default:
			printf("***buCdsaPrivKeyToCssm: bogus keyAlg\n");
			return CSSMERR_CSSM_FUNCTION_NOT_IMPLEMENTED;
	}
	
	/* create caller's key, assign blob to it */
	brtn = B_CreateKeyObject(&privKey);
	if(brtn) {
		return buBsafeErrToCssm(brtn, "B_CreateKeyObject");
	}
	buCssmDataToItem(&cdsaPrivKey->KeyData, &keyBlob);
	brtn = B_SetKeyInfo(privKey, infoType, (POINTER)&keyBlob);
	if(brtn) {
		return buBsafeErrToCssm(brtn, "B_SetKeyInfo");
	}
	*bsafePrivKey = privKey;
	return CSSM_OK;
}