RSA_DSA_keys.cpp   [plain text]


/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 * specific language governing rights and limitations under the License.
 */


/*
 * RSA_DSA_Keys.cpp - RSA, DSA related asymmetric key pair classes. 
 */

#include "RSA_DSA_keys.h"
#include <opensslUtils/opensslUtils.h>
#include <opensslUtils/opensslAsn1.h>
#include <security_cdsa_utilities/cssmdata.h>
#include <AppleCSPSession.h>
#include <AppleCSPUtils.h>
#include <assert.h>
#include <security_utilities/debugging.h>
#include "RSA_DSA_utils.h"
#include <YarrowConnection.h>
#include <security_asn1/SecNssCoder.h>

#define RSA_PUB_EXPONENT	0x10001 	/* recommended by RSA */

#define rsaKeyDebug(args...)	secdebug("rsaKey", ## args)


/***
 *** RSA-style BinaryKey
 ***/
 
/* constructor with optional existing RSA key */
/* FIXME how to transmit OAEP params? */
RSABinaryKey::RSABinaryKey(RSA *rsaKey)
	: mRsaKey(rsaKey),
	  mOaep(false),
	  mLabel(Allocator::standard())
{
}

RSABinaryKey::~RSABinaryKey()
{
	if(mRsaKey) {
		RSA_free(mRsaKey);
		mRsaKey = NULL;
	}
}

void RSABinaryKey::setOaep(
	const CSSM_DATA		&label)
{
	mLabel.copy(label);
	mOaep = true;
}

void RSABinaryKey::generateKeyBlob(
	Allocator			&allocator,
	CssmData			&blob,
	CSSM_KEYBLOB_FORMAT	&format,	/* IN/OUT */
	AppleCSPSession		&session,
	const CssmKey		*paramKey,	/* optional, unused here */
	CSSM_KEYATTR_FLAGS	&attrFlags)	/* IN/OUT */
{
	bool			isPub;
	CSSM_RETURN		crtn;
	
	/* FIXME get label from context here for OAEP */
	
	/* 
	 * Here, the incoming default of CSSM_KEYBLOB_RAW_FORMAT_NONE
	 * is translated to our AppleCSP-custom defaults. App can override.
	 */
	switch(mKeyHeader.KeyClass) {
		case CSSM_KEYCLASS_PUBLIC_KEY:
			isPub = true;
			switch(format) {
				case CSSM_KEYBLOB_RAW_FORMAT_NONE:
					format = RSA_PUB_KEY_FORMAT;	// default
					break;
				case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
					if(mOaep) {
						/* have to take digest of the whole thing including label */
						format = CSSM_KEYBLOB_RAW_FORMAT_X509;
					}
					else {
						/* calculate digest on PKCS1 blob */
						format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;	
					}
					break;
				case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
				case CSSM_KEYBLOB_RAW_FORMAT_X509:
				case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
				case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2:
					break;
				default:
					CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
			}
			break;
		case CSSM_KEYCLASS_PRIVATE_KEY:
			isPub = false;
			switch(format) {
				case CSSM_KEYBLOB_RAW_FORMAT_NONE:	// default
					format = RSA_PRIV_KEY_FORMAT;	
					break;
				case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
					if(mOaep) {
						/* have to take digest of the whole thing including label */
						format = CSSM_KEYBLOB_RAW_FORMAT_X509;
					}
					else {
						/* calculate digest on PKCS1 blob */
						format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;	
					}
					isPub = true;
					break;
				case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
				case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
				case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
					break;
				default:
					CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
			}
			break;
		default:
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
	}

	CssmAutoData encodedKey(allocator);
	if(mOaep) {
		CSSM_DATA label = mLabel;
		if(isPub) {
			crtn = RSAOAEPPublicKeyEncode(mRsaKey, &label, encodedKey);
		}
		else {
			crtn = RSAOAEPPrivateKeyEncode(mRsaKey, &label, encodedKey);
		}
	}
	else {
		if(isPub) {
			crtn = RSAPublicKeyEncode(mRsaKey, format, descData(), encodedKey);
		}
		else {
			crtn = RSAPrivateKeyEncode(mRsaKey, format, descData(), encodedKey);
		}
	}
	if(crtn) {
		CssmError::throwMe(crtn);
	}
	blob = encodedKey.release();
}
		
/***
 *** RSA-style AppleKeyPairGenContext
 ***/

/*
 * This one is specified in, and called from, CSPFullPluginSession. Our
 * only job is to prepare two subclass-specific BinaryKeys and call up to
 * AppleKeyPairGenContext.
 */
void RSAKeyPairGenContext::generate(
	const Context 	&context, 
	CssmKey 		&pubKey, 
	CssmKey 		&privKey)
{
	RSABinaryKey *pubBinKey  = new RSABinaryKey();
	RSABinaryKey *privBinKey = new RSABinaryKey();
	
	try {
		AppleKeyPairGenContext::generate(context, 
			session(),
			pubKey, 
			pubBinKey, 
			privKey, 
			privBinKey);
	}
	catch (...) {
		delete pubBinKey;
		delete privBinKey;
		throw;
	}

}
	
// this one is specified in, and called from, AppleKeyPairGenContext
void RSAKeyPairGenContext::generate(
	const Context 	&context,
	BinaryKey		&pubBinKey,	
	BinaryKey		&privBinKey,
	uint32			&keyBits)
{
	/* 
	 * These casts throw exceptions if the keys are of the 
	 * wrong classes, which would be a major bogon, since we created
	 * the keys in the above generate() function. 
	 */
	RSABinaryKey &rPubBinKey = 
		dynamic_cast<RSABinaryKey &>(pubBinKey);
	RSABinaryKey &rPrivBinKey = 
		dynamic_cast<RSABinaryKey &>(privBinKey);

	/*
	 * One parameter from context: Key size in bits is required.
	 * FIXME - get public exponent from context?
	 */
	keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH,
				CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
	if(keyBits > rsaMaxKeySize()) {
		CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
	}
	
	/* generate the private key */
	rPrivBinKey.mRsaKey = RSA_generate_key(keyBits,
		RSA_PUB_EXPONENT,
		NULL,			// no callback
		NULL);
	if(rPrivBinKey.mRsaKey == NULL) {
		rsaKeyDebug("RSA_generate_key returned NULL");
		CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);		// ???
	}
		
	/* public key is subset of private key */
	rPubBinKey.mRsaKey = RSA_new();
	if(rPrivBinKey.mRsaKey == NULL) {
		CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);	
	}
	RSA *pub = rPubBinKey.mRsaKey;
	RSA *priv = rPrivBinKey.mRsaKey;
	pub->n = BN_dup(priv->n);
	pub->e = BN_dup(priv->e);
	if((pub->n == NULL) || (pub->e == NULL)) {
		CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);		
	}
}


/***
 *** RSA-style CSPKeyInfoProvider.
 ***/
RSAKeyInfoProvider::RSAKeyInfoProvider(
	const CssmKey 	&cssmKey,
	AppleCSPSession	&session) :
		CSPKeyInfoProvider(cssmKey, session)
{
}

CSPKeyInfoProvider *RSAKeyInfoProvider::provider(
	const CssmKey 	&cssmKey,
	AppleCSPSession	&session)
{
	switch(cssmKey.algorithm()) {
		case CSSM_ALGID_RSA:
		case CSSM_ALGMODE_PKCS1_EME_OAEP:
			break;
		default:
			return NULL;
	}
	switch(cssmKey.keyClass()) {
		case CSSM_KEYCLASS_PUBLIC_KEY:
		case CSSM_KEYCLASS_PRIVATE_KEY:
			break;
		default:
			return NULL;
	}
	/* OK, we'll handle this one */
	return new RSAKeyInfoProvider(cssmKey, session);
}

/* Given a raw key, cook up a Binary key */
void RSAKeyInfoProvider::CssmKeyToBinary(
	CssmKey				*paramKey,		// ignored
	CSSM_KEYATTR_FLAGS	&attrFlags,		// IN/OUT, unused here
	BinaryKey 			**binKey)
{
	*binKey = NULL;
	RSA *rsaKey = NULL;
	CSSM_DATA label = {0, NULL};
	
	/* first cook up an RSA key */
	rsaKey = rawCssmKeyToRsa(mKey, label);
	
	/* now drop that into a BinaryKey */
	RSABinaryKey *rsaBinKey = new RSABinaryKey(rsaKey);
	*binKey = rsaBinKey;
	if(label.Data) {
		rsaBinKey->setOaep(label);
		free(label.Data);
	}
}
		
/* 
 * Obtain key size in bits.
 */
void RSAKeyInfoProvider::QueryKeySizeInBits(
	CSSM_KEY_SIZE &keySize)
{
	RSA *rsaKey = NULL;
	CSSM_DATA label = {0, NULL};
	
	if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
		CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
	}
	rsaKey = rawCssmKeyToRsa(mKey, label);
	keySize.LogicalKeySizeInBits = RSA_size(rsaKey) * 8;
	keySize.EffectiveKeySizeInBits = keySize.LogicalKeySizeInBits;
	RSA_free(rsaKey);
	if(label.Data) {
		free(label.Data);
	}	
}

/* 
 * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST 
 * passthrough.
 */
bool RSAKeyInfoProvider::getHashableBlob(
	Allocator 	&allocator,
	CssmData		&blob)			// blob to hash goes here
{
	/*
	 * The optimized case, a raw key in the "proper" format already.
	 * Only public keys in PKCS1 format fit this bill. 
	 */
	assert(mKey.blobType() == CSSM_KEYBLOB_RAW);
	bool useAsIs = false;
	
	switch(mKey.keyClass()) {
		case CSSM_KEYCLASS_PUBLIC_KEY:
			if(mKey.blobFormat() == CSSM_KEYBLOB_RAW_FORMAT_PKCS1) {
				useAsIs = true;
			}
			break;
		case CSSM_KEYCLASS_PRIVATE_KEY:
			break;
		default:
			/* shouldn't be here */
			assert(0);
			CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
	}
	if(useAsIs) {
		const CssmData &keyBlob = CssmData::overlay(mKey.KeyData);
		copyCssmData(keyBlob, blob, allocator);
		return true;
	}
	
	/* caller converts to binary and proceeds */
	return false;
}

/***
 *** DSA key support
 ***/
 

/***
 *** DSA-style BinaryKey
 ***/
 
/* constructor with optional existing DSA key */
DSABinaryKey::DSABinaryKey(DSA *dsaKey)
	: mDsaKey(dsaKey)
{
}

DSABinaryKey::~DSABinaryKey()
{
	if(mDsaKey) {
		DSA_free(mDsaKey);
		mDsaKey = NULL;
	}
}

void DSABinaryKey::generateKeyBlob(
	Allocator 		&allocator,
	CssmData			&blob,
	CSSM_KEYBLOB_FORMAT	&format,
	AppleCSPSession		&session,
	const CssmKey		*paramKey,	/* optional */
	CSSM_KEYATTR_FLAGS	&attrFlags)	/* IN/OUT */
{
	bool			isPub;
	CSSM_RETURN		crtn;
	
	/* 
	 * Here, the incoming default of CSSM_KEYBLOB_RAW_FORMAT_NONE
	 * is translated to our AppleCSP-custom defaults. App can override.
	 */
	switch(mKeyHeader.KeyClass) {
		case CSSM_KEYCLASS_PUBLIC_KEY:
			isPub = true;
			switch(format) {
				case CSSM_KEYBLOB_RAW_FORMAT_NONE:
					format = DSA_PUB_KEY_FORMAT;	// default
					break;
				case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
				case CSSM_KEYBLOB_RAW_FORMAT_X509:
				case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
				case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2:
					break;
				default:
					CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
			}
			break;
		case CSSM_KEYCLASS_PRIVATE_KEY:
			isPub = false;
			switch(format) {
				case CSSM_KEYBLOB_RAW_FORMAT_NONE:
					format = DSA_PRIV_KEY_FORMAT;	// default
					break;
				case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
					/*
					 * This is calculated on the public key, which 
					 * is not always part of a DSA private key's encoding...
					 * so first calculate the public key. 
					 */
					dsaKeyPrivToPub(mDsaKey);
					isPub = true;
					break;
				case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
				case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
				case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL:
					break;
				default:
					CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
			}
			break;
		default:
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
	}

	/* possible conversion from partial binary key to fully 
	 * formed blob */
	DSA *dsaToEncode = mDsaKey;
	DSA *dsaUpgrade = NULL;
	if(isPub &&
	   (mDsaKey->p == NULL) &&
	   (paramKey != NULL)) {
		/*
		 * Don't modify BinaryKey; make a copy.
		 */
		dsaUpgrade = DSA_new();
		if(dsaUpgrade == NULL) {
			CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);	
		}
		dsaUpgrade->pub_key = BN_dup(mDsaKey->pub_key);
		crtn = dsaGetParamsFromKey(dsaUpgrade, *paramKey, session);
		if(crtn) {
			DSA_free(dsaUpgrade);
			CssmError::throwMe(crtn);
		}
		
		/* success - switch keys and inform caller of attr change */
		dsaToEncode = dsaUpgrade;
		attrFlags &= ~CSSM_KEYATTR_PARTIAL;
	}

	/*
	 * DSA private keys originating from BSAFE form - e.g., DSA private
	 * keys wrapped in a keychain (which have format FIPS186 by default)
	 * have no public key component. Generate the public key if we don't 
	 * have one.
	 */
	if(!isPub && (dsaToEncode->pub_key == NULL)) {
		dsaKeyPrivToPub(dsaToEncode);
	}
	
	CssmAutoData 	encodedKey(allocator);
	if(isPub) {
		crtn = DSAPublicKeyEncode(dsaToEncode, format, descData(), encodedKey);
	}
	else {
		crtn = DSAPrivateKeyEncode(dsaToEncode, format, descData(), encodedKey);
	}
	if(dsaUpgrade != NULL) {
		/* temp key, get rid of it */
		DSA_free(dsaUpgrade);
	}
	if(crtn) {
		CssmError::throwMe(crtn);
	}
	blob = encodedKey.release();
}
	
/***
 *** DSA-style AppleKeyPairGenContext
 ***/

/*
 * This one is specified in, and called from, CSPFullPluginSession. Our
 * only job is to prepare two subclass-specific BinaryKeys and call up to
 * AppleKeyPairGenContext.
 */
void DSAKeyPairGenContext::generate(
	const Context 	&context, 
	CssmKey 		&pubKey, 
	CssmKey 		&privKey)
{
	DSABinaryKey *pubBinKey  = new DSABinaryKey();
	DSABinaryKey *privBinKey = new DSABinaryKey();
	
	try {
		AppleKeyPairGenContext::generate(context, 
			session(),
			pubKey, 
			pubBinKey, 
			privKey, 
			privBinKey);
	}
	catch (...) {
		delete pubBinKey;
		delete privBinKey;
		throw;
	}

}
	
/*
 * This one is specified in, and called from, AppleKeyPairGenContext
 */
void DSAKeyPairGenContext::generate(
	const Context 	&context,
	BinaryKey		&pubBinKey,	
	BinaryKey		&privBinKey,
	uint32			&keyBits)
{
	/* 
	 * These casts throw exceptions if the keys are of the 
	 * wrong classes, which would be a major bogon, since we created
	 * the keys in the above generate() function.
	 */
	DSABinaryKey &rPubBinKey = 
		dynamic_cast<DSABinaryKey &>(pubBinKey);
	DSABinaryKey &rPrivBinKey = 
		dynamic_cast<DSABinaryKey &>(privBinKey);

	/*
	 * Parameters from context: 
	 *   Key size in bits, required;
	 *   {p,q,g} from generateParams, optional
	 */
	keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH,
				CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
	if(keyBits > DSA_MAX_KEY_SIZE) {
		CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
	}
	CssmData *paramData = context.get<CssmData>(CSSM_ATTRIBUTE_ALG_PARAMS);

	NSS_DSAAlgParams algParams;
	SecNssCoder coder;			// generated algParams mallocd from here
	if(paramData != NULL) {
		/* this contains the DER encoding of a NSS_DSAAlgParams */
		CSSM_RETURN crtn = DSADecodeAlgParams(algParams, paramData->Data,
			paramData->Length, coder);
		if(crtn) {
			CssmError::throwMe(crtn);
		}
	}
	else {
		/* no alg params specified; generate them now using null (random) seed */
		dsaGenParams(keyBits, NULL, 0, algParams, coder);
	}
					
	/* create key, stuff params into it */
	rPrivBinKey.mDsaKey = DSA_new();
	if(rPrivBinKey.mDsaKey == NULL) {
		CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);		
	}
	DSA *dsaKey = rPrivBinKey.mDsaKey;
	dsaKey->p = cssmDataToBn(algParams.p);
	dsaKey->q = cssmDataToBn(algParams.q);
	dsaKey->g = cssmDataToBn(algParams.g);
	
	/* generate the key (both public and private capabilities) */
	int irtn = DSA_generate_key(dsaKey);
	if(!irtn) {
		throwRsaDsa("DSA_generate_key");
	}
	
	/* public key is subset of private key */
	rPubBinKey.mDsaKey = DSA_new();
	if(rPrivBinKey.mDsaKey == NULL) {
		CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);	
	}
	DSA *pub     = rPubBinKey.mDsaKey;
	DSA *priv    = rPrivBinKey.mDsaKey;
	pub->p       = BN_dup(priv->p);
	pub->q       = BN_dup(priv->q);
	pub->g       = BN_dup(priv->g);
	pub->pub_key = BN_dup(priv->pub_key);
	if((pub->p == NULL) || (pub->q == NULL) || (pub->g == NULL) ||
			(pub->pub_key == NULL)) {
		CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);		
	}
}

/*
 * Generate keygen parameters, stash them in a context attr array for later use
 * when actually generating the keys.
 */
void DSAKeyPairGenContext::generate(
	const Context &context, 
	uint32 bitSize,
    CssmData &params,
    uint32 &attrCount, 
	Context::Attr * &attrs)
{
	void *seed = NULL;
	unsigned seedLen = 0;

	/* optional seed from context */
	CssmData *seedData = context.get<CssmData>(CSSM_ATTRIBUTE_SEED);
	if(seedData) {
		seed = seedData->data();
		seedLen = seedData->length();
	}

	/* generate the params, temp alloc from SecNssCoder  */
	NSS_DSAAlgParams algParams;
	SecNssCoder coder;
	dsaGenParams(bitSize, seed, seedLen, algParams, coder);
	
	/*
	 * Here comes the fun part. 
	 * We "return" the DER encoding of these generated params in two ways:
	 * 1. Copy out to app via the params argument, mallocing if Data ptr is NULL.
	 *    The app must free this. 
	 * 2. Cook up a 1-element Context::attr array containing one ALG_PARAM attr,
	 *    a CSSM_DATA_PTR containing the DER encoding. We have to save a ptr to
	 *    this attr array and free it, the CSSM_DATA it points to, and the DER
	 *    encoding *that* points to, in our destructor. 
	 *
	 * First, DER encode.
	 */
	CssmAutoData aDerData(session());
	DSAEncodeAlgParams(algParams, aDerData);

	/* copy/release that into a mallocd CSSM_DATA. */
	CSSM_DATA_PTR derData = (CSSM_DATA_PTR)session().malloc(sizeof(CSSM_DATA));
	*derData = aDerData.release();
	
	/* stuff that into a one-element Attr array which we keep after returning */
	freeGenAttrs();
	mGenAttrs = (Context::Attr *)session().malloc(sizeof(Context::Attr));
	mGenAttrs->AttributeType   = CSSM_ATTRIBUTE_ALG_PARAMS;
	mGenAttrs->AttributeLength = sizeof(CSSM_DATA);
	mGenAttrs->Attribute.Data  = derData;

	/* and "return" this stuff */
	copyCssmData(CssmData::overlay(*derData), params, session());
	attrCount = 1;
	attrs = mGenAttrs;
}

/* free mGenAttrs and its referents if present */
void DSAKeyPairGenContext::freeGenAttrs()
{
	if(mGenAttrs == NULL) {
		return;
	}
	if(mGenAttrs->Attribute.Data) {
		if(mGenAttrs->Attribute.Data->Data) {
			session().free(mGenAttrs->Attribute.Data->Data);
		}
		session().free(mGenAttrs->Attribute.Data);
	}
	session().free(mGenAttrs);
}

/*
 * Generate DSA algorithm parameters from optional seed input, returning result
 * into NSS_DSAAlgParamss.[pqg]. This is called from both GenerateParameters and from
 * KeyPairGenerate (if no GenerateParameters has yet been called). 
 */
void DSAKeyPairGenContext::dsaGenParams(
	uint32				keySizeInBits,
	const void			*inSeed,		// optional
	unsigned			inSeedLen,
	NSS_DSAAlgParams 	&algParams,
	SecNssCoder			&coder)			// contents of algParams mallocd from here
{
	unsigned char seedBuf[SHA1_DIGEST_SIZE];
	void *seedPtr;
	
	/* validate key size */
	if((keySizeInBits < DSA_MIN_KEY_SIZE) || 
	   (keySizeInBits > DSA_MAX_KEY_SIZE) ||
	   (keySizeInBits & DSA_KEY_BITS_MASK)) {
		CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
	}
	
	/* seed from one of three sources */
	if(inSeed == NULL) {
		/* 20 random seed bytes */
		session().getRandomBytes(SHA1_DIGEST_SIZE, seedBuf);
		seedPtr = seedBuf;
	}
	else if(inSeedLen == SHA1_DIGEST_SIZE) {
		/* perfect */
		seedPtr = (void *)inSeed;
	}
	else {
		/* hash caller's seed */
		cspGenSha1Hash(inSeed, inSeedLen, seedBuf);
		seedPtr = seedBuf;
	}

	DSA *dsaKey = DSA_generate_parameters(keySizeInBits,
		(unsigned char *)seedPtr,	
		SHA1_DIGEST_SIZE,
		NULL,		// counter_ret
		NULL,		// h_ret
		NULL, 
		NULL);
	if(dsaKey == NULL) {
		throwRsaDsa("DSA_generate_parameters");
	}
	
	/* stuff dsaKey->[pqg] into a caller's NSS_DSAAlgParams */
	bnToCssmData(dsaKey->p, algParams.p, coder);
	bnToCssmData(dsaKey->q, algParams.q, coder);
	bnToCssmData(dsaKey->g, algParams.g, coder);
	
	DSA_free(dsaKey);
}

/***
 *** DSA-style CSPKeyInfoProvider.
 ***/
DSAKeyInfoProvider::DSAKeyInfoProvider(
	const CssmKey 	&cssmKey,
	AppleCSPSession	&session) :
		CSPKeyInfoProvider(cssmKey, session)
{

}
CSPKeyInfoProvider *DSAKeyInfoProvider::provider(
	const CssmKey 	&cssmKey,
	AppleCSPSession	&session)
{
	switch(cssmKey.algorithm()) {
		case CSSM_ALGID_DSA:
			break;
		default:
			return NULL;
	}
	switch(cssmKey.keyClass()) {
		case CSSM_KEYCLASS_PUBLIC_KEY:
		case CSSM_KEYCLASS_PRIVATE_KEY:
			break;
		default:
			return NULL;
	}
	/* OK, we'll handle this one */
	return new DSAKeyInfoProvider(cssmKey, session);
}

/* Given a raw key, cook up a Binary key */
void DSAKeyInfoProvider::CssmKeyToBinary(
	CssmKey				*paramKey,		// optional
	CSSM_KEYATTR_FLAGS	&attrFlags,		// IN/OUT
	BinaryKey 			**binKey)
{
	*binKey = NULL;
	DSA *dsaKey = NULL;
	
	/* first cook up an DSA key, then drop that into a BinaryKey */
	dsaKey = rawCssmKeyToDsa(mKey, mSession, paramKey);
	if(dsaKey->p == NULL) {
		attrFlags |= CSSM_KEYATTR_PARTIAL;
	}
	else {
		attrFlags &= ~CSSM_KEYATTR_PARTIAL;
	}
	DSABinaryKey *dsaBinKey = new DSABinaryKey(dsaKey);
	*binKey = dsaBinKey;
}
		
/* 
 * Obtain key size in bits.
 */
void DSAKeyInfoProvider::QueryKeySizeInBits(
	CSSM_KEY_SIZE &keySize)
{
	DSA *dsaKey = NULL;
	
	if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
		CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
	}
	dsaKey = rawCssmKeyToDsa(mKey,
		mSession,
		NULL);		// no param key allowed here
	if(dsaKey->p != NULL) {
		/* normal fully-formed key */
		keySize.LogicalKeySizeInBits = BN_num_bits(dsaKey->p);
		keySize.EffectiveKeySizeInBits = keySize.LogicalKeySizeInBits;
		DSA_free(dsaKey);
	}
	else {
		/* partial key, get an approximation from pub_key */
		keySize.LogicalKeySizeInBits = BN_num_bits(dsaKey->pub_key);
		DSA_free(dsaKey);
		/* and indicate this anomaly like so */
		CssmError::throwMe(CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE);
	}
}

/* 
 * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST 
 * passthrough.
 */
bool DSAKeyInfoProvider::getHashableBlob(
	Allocator 	&allocator,
	CssmData		&blob)			// blob to hash goes here
{
	/* No optimized case for DSA keys */
	return false;
}