--- /dev/null
+/*
+ * Copyright (c) 2000-2002 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.
+ */
+
+
+/*
+ * DH_keys.cpp - Diffie-Hellman key pair support.
+ */
+
+#include "DH_keys.h"
+#include "DH_utils.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 <Security/oidsalg.h>
+#include <YarrowConnection.h>
+
+#define dhKeyDebug(args...) secdebug("dhKey", ## args)
+
+/*
+ * FIXME - the CDSA Algorithm Guide claims that the incoming params argument
+ * for a GenerateAlgorithmParameters call is ignored for D-H. This means
+ * that there is no way for the caller to specify 'g' (typically 2, 3, or
+ * 5). This seems WAY bogus but we'll code to the spec for now, assuming
+ * a hard-coded default generator.
+ */
+#define DH_GENERATOR_DEFAULT DH_GENERATOR_2
+
+
+/***
+ *** Diffie-Hellman-style BinaryKey
+ ***/
+
+/* constructor with optional existing DSA key */
+DHBinaryKey::DHBinaryKey(DH *dhKey)
+ : mDhKey(dhKey)
+{
+}
+
+DHBinaryKey::~DHBinaryKey()
+{
+ if(mDhKey) {
+ DH_free(mDhKey);
+ mDhKey = NULL;
+ }
+}
+
+void DHBinaryKey::generateKeyBlob(
+ Allocator &allocator,
+ CssmData &blob,
+ CSSM_KEYBLOB_FORMAT &format,
+ AppleCSPSession &session,
+ const CssmKey *paramKey, /* optional, unused here */
+ CSSM_KEYATTR_FLAGS &attrFlags) /* IN/OUT */
+{
+
+ switch(mKeyHeader.KeyClass) {
+ case CSSM_KEYCLASS_PUBLIC_KEY:
+ {
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_NONE:
+ // take default
+ format = DH_PUB_KEY_FORMAT;
+ break;
+ case DH_PUB_KEY_FORMAT:
+ case CSSM_KEYBLOB_RAW_FORMAT_X509:
+ // proceed
+ break;
+ case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
+ /* use PKCS3 - caller won't care if we change this...right? */
+ format = DH_PUB_KEY_FORMAT;
+ break;
+ default:
+ CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
+ }
+
+ assert(mDhKey != NULL);
+ CssmAutoData encodedKey(allocator);
+ CSSM_RETURN crtn = DHPublicKeyEncode(mDhKey, format,
+ encodedKey);
+ if(crtn) {
+ CssmError::throwMe(crtn);
+ }
+ blob = encodedKey.release();
+ break;
+ }
+ case CSSM_KEYCLASS_PRIVATE_KEY:
+ {
+ switch(format) {
+ case CSSM_KEYBLOB_RAW_FORMAT_NONE:
+ // i.e., use default
+ format = DH_PRIV_KEY_FORMAT;
+ break;
+ case DH_PRIV_KEY_FORMAT:
+ case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
+ // proceed
+ break;
+
+ case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
+ {
+ /*
+ * Use public blob; calculate it if we
+ * don't already have it.
+ */
+ assert(mDhKey != NULL);
+ if(mDhKey->pub_key == NULL) {
+ int irtn = DH_generate_key(mDhKey);
+ if(!irtn) {
+ throwRsaDsa("DH_generate_key");
+ }
+ }
+ assert(mDhKey->pub_key != NULL);
+ setUpData(blob,
+ BN_num_bytes(mDhKey->pub_key),
+ *DH_Factory::privAllocator);
+ BN_bn2bin(mDhKey->pub_key, blob);
+ format = DH_PUB_KEY_FORMAT;
+ return;
+ }
+
+ default:
+ CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
+ }
+ assert(mDhKey != NULL);
+ CssmAutoData encodedKey(allocator);
+ CSSM_RETURN crtn = DHPrivateKeyEncode(mDhKey, format,
+ encodedKey);
+ if(crtn) {
+ CssmError::throwMe(crtn);
+ }
+ blob = encodedKey.release();
+ break;
+ }
+ default:
+ CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
+ }
+}
+
+/***
+ *** Diffie-Hellman 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 DHKeyPairGenContext::generate(
+ const Context &context,
+ CssmKey &pubKey,
+ CssmKey &privKey)
+{
+ DHBinaryKey *pubBinKey = new DHBinaryKey();
+ DHBinaryKey *privBinKey = new DHBinaryKey();
+
+ 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 DHKeyPairGenContext::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.
+ */
+ DHBinaryKey &rPubBinKey =
+ dynamic_cast<DHBinaryKey &>(pubBinKey);
+ DHBinaryKey &rPrivBinKey =
+ dynamic_cast<DHBinaryKey &>(privBinKey);
+
+ /*
+ * Parameters from context:
+ * Key size in bits, required;
+ * {p,g,privKeyLength} from generateParams, optional
+ * NOTE: currently the openssl D-H imnplementation ignores the
+ * privKeyLength field.
+ */
+ keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH,
+ CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
+ CssmData *paramData = context.get<CssmData>(CSSM_ATTRIBUTE_ALG_PARAMS);
+
+ NSS_DHParameterBlock algParamBlock;
+ NSS_DHParameter &algParams = algParamBlock.params;
+ uint32 privValueLen = 0; // only nonzero from externally generated
+ // params
+ SecNssCoder coder; // for temp allocs of decoded parameters
+
+ if(paramData != NULL) {
+ /* this contains the DER encoding of a DHParameterBlock */
+ CSSM_RETURN crtn;
+ crtn = DHParamBlockDecode(*paramData, algParamBlock, coder);
+ if(crtn) {
+ CssmError::throwMe(crtn);
+ }
+
+ /* snag the optional private key length field */
+ if(algParams.privateValueLength.Data) {
+ privValueLen = cssmDataToInt(algParams.privateValueLength);
+ }
+
+ /* ensure caller's key size matches the incoming params */
+ uint32 paramKeyBytes;
+ if(privValueLen) {
+ paramKeyBytes = (privValueLen + 7) / 8;
+ }
+ else {
+ paramKeyBytes = algParams.prime.Length;
+ /* trim off possible m.s. byte of zero */
+ const unsigned char *uo =
+ (const unsigned char *)algParams.prime.Data;
+ if(*uo == 0) {
+ paramKeyBytes--;
+ }
+ }
+ uint32 reqBytes = (keyBits + 7) / 8;
+ if(paramKeyBytes != reqBytes) {
+ dhKeyDebug("DH key size mismatch (req %d param %d)",
+ (int)reqBytes, (int)paramKeyBytes);
+ CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE);
+ }
+ }
+ else {
+ /* no alg params specified; generate them now */
+ dhKeyDebug("DH implicit alg param calculation");
+ memset(&algParamBlock, 0, sizeof(algParamBlock));
+ dhGenParams(keyBits, DH_GENERATOR_DEFAULT, 0, algParams, coder);
+ }
+
+ /* create key, stuff params into it */
+ rPrivBinKey.mDhKey = DH_new();
+ if(rPrivBinKey.mDhKey == NULL) {
+ CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
+ }
+ DH *dhKey = rPrivBinKey.mDhKey;
+ dhKey->p = cssmDataToBn(algParams.prime);
+ dhKey->g = cssmDataToBn(algParams.base);
+ dhKey->length = privValueLen;
+ cspDhDebug("private DH binary key dhKey %p", dhKey);
+
+ /* generate the key (both public and private capabilities) */
+ int irtn = DH_generate_key(dhKey);
+ if(!irtn) {
+ throwRsaDsa("DH_generate_key");
+ }
+
+ /* public key is a subset */
+ rPubBinKey.mDhKey = DH_new();
+ if(rPubBinKey.mDhKey == NULL) {
+ CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
+ }
+ DH *pubDhKey = rPubBinKey.mDhKey;
+ pubDhKey->pub_key = BN_dup(dhKey->pub_key);
+ /* these params used for X509 style key blobs */
+ pubDhKey->p = BN_dup(dhKey->p);
+ pubDhKey->g = BN_dup(dhKey->g);
+ cspDhDebug("public DH binary key pubDhKey %p", pubDhKey);
+}
+
+
+
+/***
+ *** Diffie-Hellman CSPKeyInfoProvider.
+ ***/
+DHKeyInfoProvider::DHKeyInfoProvider(
+ const CssmKey &cssmKey,
+ AppleCSPSession &session) :
+ CSPKeyInfoProvider(cssmKey, session)
+{
+}
+
+CSPKeyInfoProvider *DHKeyInfoProvider::provider(
+ const CssmKey &cssmKey,
+ AppleCSPSession &session)
+{
+ switch(cssmKey.algorithm()) {
+ case CSSM_ALGID_DH:
+ break;
+ default:
+ CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
+ }
+ switch(cssmKey.keyClass()) {
+ case CSSM_KEYCLASS_PUBLIC_KEY:
+ case CSSM_KEYCLASS_PRIVATE_KEY:
+ break;
+ default:
+ CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
+ }
+ /* OK, we'll handle this one */
+ return new DHKeyInfoProvider(cssmKey, session);
+}
+
+/* Given a raw key, cook up a Binary key */
+void DHKeyInfoProvider::CssmKeyToBinary(
+ CssmKey *paramKey, // optional, ignored here
+ CSSM_KEYATTR_FLAGS &attrFlags, // IN/OUT
+ BinaryKey **binKey)
+{
+ *binKey = NULL;
+
+ assert(mKey.blobType() == CSSM_KEYBLOB_RAW);
+ switch(mKey.keyClass()) {
+ case CSSM_KEYCLASS_PUBLIC_KEY:
+ case CSSM_KEYCLASS_PRIVATE_KEY:
+ break;
+ default:
+ CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
+ }
+
+ /* first cook up an DH key, then drop that into a BinaryKey */
+ DH *dhKey = rawCssmKeyToDh(mKey);
+ DHBinaryKey *dhBinKey = new DHBinaryKey(dhKey);
+ *binKey = dhBinKey;
+ cspDhDebug("CssmKeyToBinary dhKey %p", dhKey);
+}
+
+/*
+ * Obtain key size in bits.
+ * FIXME - I doubt that this is, or can be, exactly accurate.....
+ */
+void DHKeyInfoProvider::QueryKeySizeInBits(
+ CSSM_KEY_SIZE &keySize)
+{
+ uint32 numBits = 0;
+
+ if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
+ CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
+ }
+ DH *dhKey = rawCssmKeyToDh(mKey);
+
+ /* DH_size requires the p parameter, which some public keys don't have */
+ if(dhKey->p != NULL) {
+ numBits = DH_size(dhKey) * 8;
+ }
+ else {
+ assert(dhKey->pub_key != NULL);
+ numBits = BN_num_bytes(dhKey->pub_key) * 8;
+ }
+ DH_free(dhKey);
+ keySize.LogicalKeySizeInBits = numBits;
+ keySize.EffectiveKeySizeInBits = numBits;
+}
+
+/*
+ * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST
+ * passthrough.
+ */
+bool DHKeyInfoProvider::getHashableBlob(
+ Allocator &allocator,
+ CssmData &blob) // blob to hash goes here
+{
+ /*
+ * The optimized case, a raw key in the "proper" format already.
+ */
+ assert(mKey.blobType() == CSSM_KEYBLOB_RAW);
+ bool useAsIs = false;
+
+ switch(mKey.keyClass()) {
+ case CSSM_KEYCLASS_PUBLIC_KEY:
+ if(mKey.blobFormat() == CSSM_KEYBLOB_RAW_FORMAT_PKCS3) {
+ 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;
+}
+
+/*
+ * Generate keygen parameters, stash them in a context attr array for later use
+ * when actually generating the keys.
+ */
+
+void DHKeyPairGenContext::generate(
+ const Context &context,
+ uint32 bitSize,
+ CssmData ¶ms, // RETURNED here,
+ uint32 &attrCount, // here,
+ Context::Attr * &attrs) // and here
+{
+ /* generate the params */
+ NSS_DHParameterBlock algParamBlock;
+ SecNssCoder coder;
+ NSS_DHParameter &algParams = algParamBlock.params;
+ dhGenParams(bitSize, DH_GENERATOR_DEFAULT, 0, algParams, coder);
+
+ /* drop in the required OID */
+ algParamBlock.oid = CSSMOID_PKCS3;
+
+ /*
+ * 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());
+ PRErrorCode perr;
+ perr = SecNssEncodeItemOdata(&algParamBlock, kSecAsn1DHParameterBlockTemplate,
+ aDerData);
+ if(perr) {
+ /* only known error... */
+ CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
+ }
+
+ /* 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 DHKeyPairGenContext::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 returning result
+ * into DHParameter.{prime,base,privateValueLength].
+ * This is called from both GenerateParameters and from
+ * KeyPairGenerate (if no GenerateParameters has yet been called).
+ *
+ * FIXME - privateValueLength not implemented in openssl, not here
+ * either for now.
+ */
+
+void DHKeyPairGenContext::dhGenParams(
+ uint32 keySizeInBits,
+ unsigned g, // probably should be BIGNUM
+ int privValueLength, // optional
+ NSS_DHParameter &algParams,
+ SecNssCoder &coder) // temp contents of algParams
+ // mallocd here
+{
+ /* validate key size */
+ if((keySizeInBits < DH_MIN_KEY_SIZE) ||
+ (keySizeInBits > DH_MAX_KEY_SIZE)) {
+ CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
+ }
+
+ /* create an openssl-style DH key with minimal setup */
+ DH *dhKey = DH_generate_parameters(keySizeInBits, g, NULL, NULL);
+ if(dhKey == NULL) {
+ throwRsaDsa("DSA_generate_parameters");
+ }
+
+ /* stuff dhKey->{p,g,length}] into a caller's NSS_DHParameter */
+ bnToCssmData(dhKey->p, algParams.prime, coder);
+ bnToCssmData(dhKey->g, algParams.base, coder);
+ CSSM_DATA &privValData = algParams.privateValueLength;
+ if(privValueLength) {
+ intToCssmData(privValueLength, privValData, coder);
+ }
+ else {
+ privValData.Data = NULL;
+ privValData.Length = 0;
+ }
+ DH_free(dhKey);
+}
+