]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_apple_csp/lib/RSA_DSA_keys.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_apple_csp / lib / RSA_DSA_keys.cpp
diff --git a/libsecurity_apple_csp/lib/RSA_DSA_keys.cpp b/libsecurity_apple_csp/lib/RSA_DSA_keys.cpp
new file mode 100644 (file)
index 0000000..7b9deb5
--- /dev/null
@@ -0,0 +1,829 @@
+/*
+ * 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;
+}