]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_ssl/lib/appleCdsa.c
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_ssl / lib / appleCdsa.c
diff --git a/libsecurity_ssl/lib/appleCdsa.c b/libsecurity_ssl/lib/appleCdsa.c
new file mode 100644 (file)
index 0000000..b31364d
--- /dev/null
@@ -0,0 +1,2147 @@
+/*
+ * Copyright (c) 2000-2001,2005-2008,2010-2012 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The 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.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * appleCdsa.c - CDSA based implementation of sslCrypto.h interfaces.
+ */
+
+#include "ssl.h"
+#if USE_CDSA_CRYPTO
+
+#include "appleCdsa.h"
+#include "sslCrypto.h"
+
+#include "CipherSuite.h"
+#include "sslContext.h"
+#include "sslMemory.h"
+#include "sslUtils.h"
+#include "sslDebug.h"
+#include "sslBER.h"
+#include "ModuleAttacher.h"
+
+#ifndef        _SSL_KEYCHAIN_H_
+#include "sslKeychain.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <Security/cssm.h>
+#include <Security/cssmapple.h>
+#include <Security/Security.h>
+#include <Security/SecTrustPriv.h>
+#include <Security/SecPolicyPriv.h>
+#include <Security/SecKeyPriv.h>
+
+/* X.509 includes, from cssmapi */
+#include <Security/x509defs.h>         /* x.509 function and type defs */
+#include <Security/oidsalg.h>
+#include <Security/oidscert.h>
+
+#pragma mark -
+#pragma mark Utilities
+
+/*
+ * Set up a Raw symmetric key with specified algorithm and key bits.
+ */
+OSStatus sslSetUpSymmKey(
+       CSSM_KEY_PTR    symKey,
+       CSSM_ALGORITHMS alg,
+       CSSM_KEYUSE             keyUse,                 // CSSM_KEYUSE_ENCRYPT, etc.
+       CSSM_BOOL               copyKey,                // true: copy keyData   false: set by reference
+       uint8                   *keyData,
+       size_t          keyDataLen)             // in bytes
+{
+       OSStatus serr;
+       CSSM_KEYHEADER *hdr;
+
+       memset(symKey, 0, sizeof(CSSM_KEY));
+       if(copyKey) {
+               serr = stSetUpCssmData(&symKey->KeyData, keyDataLen);
+               if(serr) {
+                       return serr;
+               }
+               memmove(symKey->KeyData.Data, keyData, keyDataLen);
+       }
+       else {
+               symKey->KeyData.Data = keyData;
+               symKey->KeyData.Length = keyDataLen;
+       }
+
+       /* set up the header */
+       hdr = &symKey->KeyHeader;
+       hdr->BlobType = CSSM_KEYBLOB_RAW;
+       hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
+       hdr->AlgorithmId = alg;
+       hdr->KeyClass = CSSM_KEYCLASS_SESSION_KEY;
+       hdr->LogicalKeySizeInBits = keyDataLen * 8;
+       hdr->KeyAttr = CSSM_KEYATTR_MODIFIABLE | CSSM_KEYATTR_EXTRACTABLE;
+       hdr->KeyUsage = keyUse;
+       hdr->WrapAlgorithmId = CSSM_ALGID_NONE;
+       return noErr;
+}
+
+/*
+ * Free a CSSM_KEY - its CSP resources, KCItemRef, and the key itself.
+ */
+OSStatus sslFreeKey(
+       CSSM_CSP_HANDLE         cspHand,
+       CSSM_KEY_PTR            *key,           /* so we can null it out */
+       #if             ST_KC_KEYS_NEED_REF
+       SecKeychainRef  *kcItem)
+       #else
+       void                    *kcItem)
+       #endif
+{
+       assert(key != NULL);
+
+       if(*key != NULL) {
+               if(cspHand != 0) {
+                       CSSM_FreeKey(cspHand, NULL, *key, CSSM_FALSE);
+               }
+               stAppFree(*key, NULL);          // key mallocd by CL using our callback
+               *key = NULL;
+       }
+       #if             ST_KC_KEYS_NEED_REF
+       if((kcItem != NULL) && (*kcItem != NULL)) {
+               KCReleaseItem(kcItem);          /* does this NULL the referent? */
+               *kcItem = NULL;
+       }
+       #endif
+       return noErr;
+}
+
+/*
+ * Standard app-level memory functions required by CDSA.
+ */
+void * stAppMalloc (size_t size, void *allocRef) {
+       return( malloc(size) );
+}
+void stAppFree (void *mem_ptr, void *allocRef) {
+       free(mem_ptr);
+       return;
+}
+void * stAppRealloc (void *ptr, size_t size, void *allocRef) {
+       return( realloc( ptr, size ) );
+}
+void * stAppCalloc (uint32_t num, size_t size, void *allocRef) {
+       return( calloc( num, size ) );
+}
+
+/*
+ * Ensure there's a connection to ctx->cspHand. If there
+ * already is one, fine.
+ * Note that as of 12/18/00, we assume we're connected to
+ * all modules all the time (since we do an attachToAll() in
+ * SSLNewContext()).
+ */
+OSStatus attachToCsp(SSLContext *ctx)
+{
+       assert(ctx != NULL);
+       if(ctx->cspHand != 0) {
+               return noErr;
+       }
+       else {
+               return errSSLModuleAttach;
+       }
+}
+
+/*
+ * Connect to TP, CL; reusable.
+ */
+OSStatus attachToCl(SSLContext *ctx)
+{
+       assert(ctx != NULL);
+       if(ctx->clHand != 0) {
+               return noErr;
+       }
+       else {
+               return errSSLModuleAttach;
+       }
+}
+
+OSStatus attachToTp(SSLContext *ctx)
+{
+       assert(ctx != NULL);
+       if(ctx->tpHand != 0) {
+               return noErr;
+       }
+       else {
+               return errSSLModuleAttach;
+       }
+}
+
+/*
+ * Convenience function - attach to CSP, CL, TP. Reusable.
+ */
+OSStatus attachToAll(SSLContext *ctx)
+{
+       CSSM_RETURN crtn;
+
+       assert(ctx != NULL);
+       crtn = attachToModules(&ctx->cspHand, &ctx->clHand, &ctx->tpHand);
+       if(crtn) {
+          return errSSLModuleAttach;
+       }
+       else {
+               return noErr;
+       }
+}
+
+OSStatus detachFromAll(SSLContext *ctx)
+{
+       #if     0
+       /* No more, attachments are kept on a global basis */
+       assert(ctx != NULL);
+       if(ctx->cspHand != 0) {
+               CSSM_ModuleDetach(ctx->cspHand);
+               ctx->cspHand = 0;
+       }
+       if(ctx->tpHand != 0) {
+               CSSM_ModuleDetach(ctx->tpHand);
+               ctx->tpHand = 0;
+       }
+       if(ctx->clHand != 0) {
+               CSSM_ModuleDetach(ctx->clHand);
+               ctx->clHand = 0;
+       }
+       #endif  /* 0 */
+       return noErr;
+}
+
+/*
+ * Add a CSSM_ATTRIBUTE_RSA_BLINDING attribute to
+ * specified crypto context.
+ */
+static CSSM_RETURN sslAddBlindingAttr(
+       CSSM_CC_HANDLE ccHand)
+{
+       CSSM_CONTEXT_ATTRIBUTE  newAttr;
+       CSSM_RETURN                             crtn;
+
+       newAttr.AttributeType     = CSSM_ATTRIBUTE_RSA_BLINDING;
+       newAttr.AttributeLength   = sizeof(uint32_t);
+       newAttr.Attribute.Uint32  = 1;
+       crtn = CSSM_UpdateContextAttributes(ccHand, 1, &newAttr);
+       if(crtn) {
+               stPrintCdsaError("CSSM_UpdateContextAttributes", crtn);
+       }
+       return crtn;
+}
+
+/* Get CSP, key in CSSM format from a SecKeyRef */
+static OSStatus sslGetKeyParts(
+       SecKeyRef                       keyRef,
+       const CSSM_KEY          **cssmKey,
+       CSSM_CSP_HANDLE         *cspHand)
+{
+       OSStatus ortn = SecKeyGetCSSMKey(keyRef, cssmKey);
+       if(ortn) {
+               sslErrorLog("sslGetKeyParts: SecKeyGetCSSMKey err %d\n",
+                       (int)ortn);
+               return ortn;
+       }
+       ortn = SecKeyGetCSPHandle(keyRef, cspHand);
+       if(ortn) {
+               sslErrorLog("sslGetKeyParts: SecKeyGetCSPHandle err %d\n",
+                       (int)ortn);
+       }
+       return ortn;
+}
+
+/* Return the first certificate reference from the supplied array
+ * whose data matches the given certificate, or NULL if none match.
+ */
+ static SecCertificateRef sslGetMatchingCertInArray(
+       SecCertificateRef       certRef,
+       CFArrayRef          certArray)
+{
+    CSSM_DATA certData;
+    OSStatus ortn;
+    int idx, count;
+
+    if(certRef == NULL || certArray == NULL) {
+        return NULL;
+    }
+    ortn = SecCertificateGetData(certRef, &certData);
+    if(!ortn) {
+        count = CFArrayGetCount(certArray);
+        for(idx=0; idx<count; idx++) {
+            CSSM_DATA aData = { 0, NULL };
+            SecCertificateRef aCert = (SecCertificateRef)CFArrayGetValueAtIndex(certArray, idx);
+            ortn = SecCertificateGetData(aCert, &aData);
+            if (!ortn && aData.Length == certData.Length &&
+                !memcmp(aData.Data, certData.Data, certData.Length)) {
+                return aCert;
+            }
+        }
+       }
+    return NULL;
+}
+
+#pragma mark -
+#pragma mark CSSM_DATA routines
+
+CSSM_DATA_PTR stMallocCssmData(
+       size_t size)
+{
+       CSSM_DATA_PTR rtn = (CSSM_DATA_PTR)stAppMalloc(sizeof(CSSM_DATA), NULL);
+
+       if(rtn == NULL) {
+               return NULL;
+       }
+       rtn->Length = size;
+       if(size == 0) {
+               rtn->Data = NULL;
+       }
+       else {
+               rtn->Data = (uint8 *)stAppMalloc(size, NULL);
+       }
+       return rtn;
+}
+
+void stFreeCssmData(
+       CSSM_DATA_PTR data,
+       CSSM_BOOL freeStruct)
+{
+       if(data == NULL) {
+               return;
+       }
+       if(data->Data != NULL) {
+               stAppFree(data->Data, NULL);
+               data->Data   = NULL;
+       }
+       data->Length = 0;
+       if(freeStruct) {
+               stAppFree(data, NULL);
+       }
+}
+
+/*
+ * Ensure that indicated CSSM_DATA_PTR can handle 'length' bytes of data.
+ * Malloc the Data ptr if necessary.
+ */
+OSStatus stSetUpCssmData(
+       CSSM_DATA_PTR   data,
+       size_t          length)
+{
+       assert(data != NULL);
+       if(data->Length == 0) {
+               data->Data = (uint8 *)stAppMalloc(length, NULL);
+               if(data->Data == NULL) {
+                       return memFullErr;
+               }
+       }
+       else if(data->Length < length) {
+               sslErrorLog("stSetUpCssmData: length too small\n");
+               return memFullErr;
+       }
+       data->Length = length;
+       return noErr;
+}
+
+/* All signature ops are "raw", with digest step done by us */
+static OSStatus sslKeyToSigAlg(
+       const CSSM_KEY *cssmKey,
+       CSSM_ALGORITHMS *sigAlg)        /* RETURNED */
+
+{
+       OSStatus ortn = noErr;
+       switch(cssmKey->KeyHeader.AlgorithmId) {
+               case CSSM_ALGID_RSA:
+                       *sigAlg = CSSM_ALGID_RSA;
+                       break;
+               case CSSM_ALGID_DSA:
+                       *sigAlg = CSSM_ALGID_DSA;
+                       break;
+               case CSSM_ALGID_ECDSA:
+                       *sigAlg = CSSM_ALGID_ECDSA;
+                       break;
+               default:
+                       ortn = errSSLBadConfiguration;
+                       break;
+       }
+       return ortn;
+}
+
+#pragma mark -
+#pragma mark Public CSP Functions
+
+/*
+ * Raw RSA/DSA sign/verify.
+ */
+OSStatus sslRawSign(
+       SSLContext              *ctx,
+       SecKeyRef               privKeyRef,
+       const UInt8             *plainText,
+       size_t                  plainTextLen,
+       UInt8                   *sig,                   // mallocd by caller; RETURNED
+       size_t                  sigLen,                 // available
+       size_t                  *actualBytes)   // RETURNED
+{
+       CSSM_CC_HANDLE                  sigHand = 0;
+       CSSM_RETURN                             crtn;
+       OSStatus                                serr;
+       CSSM_DATA                               sigData;
+       CSSM_DATA                               ptextData;
+       CSSM_CSP_HANDLE                 cspHand;
+       const CSSM_KEY                  *privKey;
+       const CSSM_ACCESS_CREDENTIALS   *creds;
+
+       assert(ctx != NULL);
+       if((privKeyRef == NULL) ||
+          (plainText == NULL)  ||
+          (sig == NULL)                ||
+          (actualBytes == NULL)) {
+               sslErrorLog("sslRsaRawSign: bad arguments\n");
+               return errSSLInternal;
+       }
+       *actualBytes = 0;
+
+       /* Get CSP, signing key in CSSM format */
+       serr = sslGetKeyParts(privKeyRef, &privKey, &cspHand);
+       if(serr) {
+               return serr;
+       }
+       assert(privKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY);
+
+       CSSM_ALGORITHMS sigAlg;
+       serr = sslKeyToSigAlg(privKey, &sigAlg);
+       if(serr) {
+               return serr;
+       }
+
+       /*
+        * Get default creds
+        * FIXME: per 3420180, this needs to allow app-specified creds via
+        * an new API
+        */
+       serr = SecKeyGetCredentials(privKeyRef,
+               CSSM_ACL_AUTHORIZATION_SIGN,
+               kSecCredentialTypeDefault,
+               &creds);
+       if(serr) {
+               sslErrorLog("sslRawSign: SecKeyGetCredentials err %lu\n", (unsigned long)serr);
+               return serr;
+       }
+
+       crtn = CSSM_CSP_CreateSignatureContext(cspHand,
+               sigAlg,
+               creds,
+               privKey,
+               &sigHand);
+       if(crtn) {
+               stPrintCdsaError("CSSM_CSP_CreateSignatureContext (1)", crtn);
+               return errSSLCrypto;
+       }
+
+       if((ctx->rsaBlindingEnable) &&
+          (privKey->KeyHeader.AlgorithmId == CSSM_ALGID_RSA)) {
+               /*
+                * Turn on RSA blinding to defeat timing attacks
+                */
+               crtn = sslAddBlindingAttr(sigHand);
+               if(crtn) {
+                       return crtn;
+               }
+       }
+
+       ptextData.Data = (uint8 *)plainText;
+       ptextData.Length = plainTextLen;
+
+       /* caller better get this right, or the SignData will fail */
+       sigData.Data = sig;
+       sigData.Length = sigLen;
+
+       crtn = CSSM_SignData(sigHand,
+               &ptextData,
+               1,
+               CSSM_ALGID_NONE,        // digestAlg for raw sign
+               &sigData);
+       if(crtn) {
+               stPrintCdsaError("CSSM_SignData", crtn);
+               serr = errSSLCrypto;
+       }
+       else {
+               *actualBytes = sigData.Length;
+               serr = noErr;
+       }
+       if(sigHand != 0) {
+               CSSM_DeleteContext(sigHand);
+       }
+       return serr;
+}
+
+OSStatus sslRawVerify(
+       SSLContext                      *ctx,
+       const SSLPubKey     *sslPubKey,
+       const UInt8                     *plainText,
+       size_t                          plainTextLen,
+       const UInt8                     *sig,
+       size_t                          sigLen)
+{
+       CSSM_CC_HANDLE                  sigHand = 0;
+       CSSM_RETURN                             crtn;
+       OSStatus                                serr;
+       CSSM_DATA                               sigData;
+       CSSM_DATA                               ptextData;
+       const CSSM_KEY          *pubKey;
+       CSSM_CSP_HANDLE         cspHand;
+
+       assert(ctx != NULL);
+       if((sslPubKey == NULL)       ||
+          (sslPubKey->key == NULL)  ||
+          (sslPubKey->csp == 0)     ||
+          (plainText == NULL)       ||
+          (sig == NULL)) {
+               sslErrorLog("sslRawVerify: bad arguments\n");
+               return errSSLInternal;
+       }
+
+       pubKey = &sslPubKey->key;
+       cspHand = sslPubKey->csp;
+
+       CSSM_ALGORITHMS sigAlg;
+       serr = sslKeyToSigAlg(pubKey, &sigAlg);
+       if(serr) {
+               return serr;
+       }
+       crtn = CSSM_CSP_CreateSignatureContext(cspHand,
+               sigAlg,
+               NULL,                           // passPhrase
+               pubKey,
+               &sigHand);
+       if(sigHand == 0) {
+               stPrintCdsaError("CSSM_CSP_CreateSignatureContext (2)", crtn);
+               return errSSLCrypto;
+       }
+
+       ptextData.Data = (uint8 *)plainText;
+       ptextData.Length = plainTextLen;
+       sigData.Data = (uint8 *)sig;
+       sigData.Length = sigLen;
+
+       crtn = CSSM_VerifyData(sigHand,
+               &ptextData,
+               1,
+               CSSM_ALGID_NONE,                // digestAlg
+               &sigData);
+       if(crtn) {
+               stPrintCdsaError("CSSM_VerifyData", crtn);
+               serr = errSSLCrypto;
+       }
+       else {
+               serr = noErr;
+       }
+       if(sigHand != 0) {
+               CSSM_DeleteContext(sigHand);
+       }
+       return serr;
+}
+
+/*
+ * Encrypt/Decrypt
+ */
+OSStatus sslRsaEncrypt(
+       SSLContext                      *ctx,
+       const CSSM_KEY          *pubKey,
+       CSSM_CSP_HANDLE         cspHand,
+       CSSM_PADDING            padding,                        // CSSM_PADDING_PKCS1, CSSM_PADDING_APPLE_SSLv2
+       const UInt8                     *plainText,
+       size_t                          plainTextLen,
+       UInt8                           *cipherText,            // mallocd by caller; RETURNED
+       size_t                          cipherTextLen,          // available
+       size_t                          *actualBytes)           // RETURNED
+{
+       CSSM_DATA               ctextData = {0, NULL};
+       CSSM_DATA               ptextData;
+       CSSM_DATA               remData = {0, NULL};
+       CSSM_CC_HANDLE  cryptHand = 0;
+       OSStatus                serr = errSSLInternal;
+       CSSM_RETURN             crtn;
+       size_t                  bytesMoved = 0;
+       CSSM_ACCESS_CREDENTIALS creds;
+
+       assert(ctx != NULL);
+       assert(actualBytes != NULL);
+       *actualBytes = 0;
+
+       if((pubKey == NULL) || (cspHand == 0)) {
+               sslErrorLog("sslRsaEncrypt: bad pubKey/cspHand\n");
+               return errSSLInternal;
+       }
+       assert(pubKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY);
+
+       #if             RSA_PUB_KEY_USAGE_HACK
+       ((CSSM_KEY_PTR)pubKey)->KeyHeader.KeyUsage |= CSSM_KEYUSE_ENCRYPT;
+       #endif
+       memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+
+       crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
+               CSSM_ALGID_RSA,
+               &creds,
+               pubKey,
+               padding,
+               &cryptHand);
+       if(crtn) {
+               stPrintCdsaError("CSSM_CSP_CreateAsymmetricContext", crtn);
+               return errSSLCrypto;
+       }
+       ptextData.Data = (uint8 *)plainText;
+       ptextData.Length = plainTextLen;
+
+       /*
+        * Have CSP malloc ciphertext
+        */
+       crtn = CSSM_EncryptData(cryptHand,
+               &ptextData,
+               1,
+               &ctextData,
+               1,
+               &bytesMoved,
+               &remData);
+       if(crtn == CSSM_OK) {
+               /*
+                * ciphertext in both ctextData and remData; ensure it'll fit
+                * in caller's buf & copy
+                */
+               if(bytesMoved > cipherTextLen) {
+                       sslErrorLog("sslRsaEncrypt overflow; cipherTextLen %lu bytesMoved %lu\n",
+                               cipherTextLen, bytesMoved);
+                       serr = errSSLCrypto;
+               }
+               else {
+                       size_t toMoveCtext;
+                       size_t toMoveRem;
+
+                       *actualBytes = bytesMoved;
+                       /*
+                        * Snag valid data from ctextData - its length or bytesMoved,
+                        * whichever is less
+                        */
+                       if(ctextData.Length > bytesMoved) {
+                               /* everything's in ctext */
+                               toMoveCtext = bytesMoved;
+                               toMoveRem = 0;
+                       }
+                       else {
+                               /* must be some in remData too */
+                               toMoveCtext = ctextData.Length;
+                               toMoveRem = bytesMoved - toMoveCtext;           // remainder
+                       }
+                       if(toMoveCtext) {
+                               memmove(cipherText, ctextData.Data, toMoveCtext);
+                       }
+                       if(toMoveRem) {
+                               memmove(cipherText + toMoveCtext, remData.Data,
+                                       toMoveRem);
+                       }
+                       serr = noErr;
+               }
+       }
+       else {
+               stPrintCdsaError("CSSM_EncryptData", crtn);
+               serr = errSSLCrypto;
+       }
+       if(cryptHand != 0) {
+               CSSM_DeleteContext(cryptHand);
+       }
+
+       /* free data mallocd by CSP */
+       stFreeCssmData(&ctextData, CSSM_FALSE);
+       stFreeCssmData(&remData, CSSM_FALSE);
+       return serr;
+}
+
+OSStatus sslRsaDecrypt(
+       SSLContext                      *ctx,
+       SecKeyRef                       privKeyRef,
+       CSSM_PADDING            padding,                        // CSSM_PADDING_PKCS1, CSSM_PADDING_APPLE_SSLv2
+       const UInt8                     *cipherText,
+       size_t                          cipherTextLen,
+       UInt8                           *plainText,                     // mallocd by caller; RETURNED
+       size_t                          plainTextLen,           // available
+       size_t                          *actualBytes)           // RETURNED
+{
+       CSSM_DATA               ptextData = {0, NULL};
+       CSSM_DATA               ctextData;
+       CSSM_DATA               remData = {0, NULL};
+       CSSM_CC_HANDLE  cryptHand = 0;
+       OSStatus                serr = errSSLInternal;
+       CSSM_RETURN             crtn;
+       size_t                  bytesMoved = 0;
+       CSSM_CSP_HANDLE cspHand;
+       const CSSM_KEY  *privKey;
+       const CSSM_ACCESS_CREDENTIALS   *creds;
+
+       assert(ctx != NULL);
+       assert(actualBytes != NULL);
+       *actualBytes = 0;
+
+       if(privKeyRef == NULL) {
+               sslErrorLog("sslRsaDecrypt: bad privKey\n");
+               return errSSLInternal;
+       }
+
+       /* Get CSP, signing key in CSSM format */
+       serr = sslGetKeyParts(privKeyRef, &privKey, &cspHand);
+       if(serr) {
+               return serr;
+       }
+       assert(privKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY);
+
+       /*
+        * Get default creds
+        * FIXME: per 3420180, this needs to allow app-specified creds via
+        * an new API
+        */
+       serr = SecKeyGetCredentials(privKeyRef,
+               CSSM_ACL_AUTHORIZATION_DECRYPT,
+               kSecCredentialTypeDefault,
+               &creds);
+       if(serr) {
+               sslErrorLog("sslRsaDecrypt: SecKeyGetCredentials err %lu\n", (unsigned long)serr);
+               return serr;
+       }
+       crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
+               CSSM_ALGID_RSA,
+               creds,
+               privKey,
+               padding,
+               &cryptHand);
+       if(crtn) {
+               stPrintCdsaError("CSSM_CSP_CreateAsymmetricContext", crtn);
+               return errSSLCrypto;
+       }
+       ctextData.Data = (uint8 *)cipherText;
+       ctextData.Length = cipherTextLen;
+
+       if((ctx->rsaBlindingEnable) &&
+          (privKey->KeyHeader.AlgorithmId == CSSM_ALGID_RSA)) {
+               /*
+                * Turn on RSA blinding to defeat timing attacks
+                */
+               crtn = sslAddBlindingAttr(cryptHand);
+               if(crtn) {
+                       return crtn;
+               }
+       }
+
+       /*
+        * Have CSP malloc plaintext
+        */
+       crtn = CSSM_DecryptData(cryptHand,
+               &ctextData,
+               1,
+               &ptextData,
+               1,
+               &bytesMoved,
+               &remData);
+       if(crtn == CSSM_OK) {
+               /*
+                * plaintext in both ptextData and remData; ensure it'll fit
+                * in caller's buf & copy
+                */
+               if(bytesMoved > plainTextLen) {
+                       sslErrorLog("sslRsaDecrypt overflow; plainTextLen %lu bytesMoved %lu\n",
+                               plainTextLen, bytesMoved);
+                       serr = errSSLCrypto;
+               }
+               else {
+                       size_t toMovePtext;
+                       size_t toMoveRem;
+
+                       *actualBytes = bytesMoved;
+                       /*
+                        * Snag valid data from ptextData - its length or bytesMoved,
+                        * whichever is less
+                        */
+                       if(ptextData.Length > bytesMoved) {
+                               /* everything's in ptext */
+                               toMovePtext = bytesMoved;
+                               toMoveRem = 0;
+                       }
+                       else {
+                               /* must be some in remData too */
+                               toMovePtext = ptextData.Length;
+                               toMoveRem = bytesMoved - toMovePtext;           // remainder
+                       }
+                       if(toMovePtext) {
+                               memmove(plainText, ptextData.Data, toMovePtext);
+                       }
+                       if(toMoveRem) {
+                               memmove(plainText + toMovePtext, remData.Data,
+                                       toMoveRem);
+                       }
+                       serr = noErr;
+               }
+       }
+       else {
+               stPrintCdsaError("CSSM_DecryptData", crtn);
+               serr = errSSLCrypto;
+       }
+       if(cryptHand != 0) {
+               CSSM_DeleteContext(cryptHand);
+       }
+
+       /* free data mallocd by CSP */
+       stFreeCssmData(&ptextData, CSSM_FALSE);
+       stFreeCssmData(&remData, CSSM_FALSE);
+       return serr;
+}
+
+/*
+ * Obtain size of key in bytes.
+ */
+uint32_t sslPrivKeyLengthInBytes(const SSLPrivKey *sslKey)
+{
+    const CSSM_KEY *cssmKey;
+
+       assert(sslKey != NULL);
+       assert(sslKey->key != NULL);
+       err = SecKeyGetCSSMKey(sslKey->key, &cssmKey);
+       if(err) {
+               sslErrorLog("sslKeyLengthInBytes: SecKeyGetCSSMKey err %d\n", (int)err);
+               return 0;
+       }
+
+       return (((cssmKey->KeyHeader.LogicalKeySizeInBits) + 7) / 8);
+}
+
+/*
+ * Obtain size of key in bytes.
+ */
+uint32_t sslPubKeyLengthInBytes(const SSLPubKey *sslKey)
+{
+    const CSSM_KEY *cssmKey;
+
+       assert(sslKey != NULL);
+       assert(sslKey->key != NULL);
+
+       return (((sslKey->key.KeyHeader.LogicalKeySizeInBits) + 7) / 8);
+}
+
+
+/*
+ * Obtain maximum size of signature in bytes. A bit of a kludge; we could
+ * ask the CSP to do this but that would be kind of expensive.
+ */
+OSStatus sslGetMaxSigSize(
+       const CSSM_KEY  *privKey,
+       uint32_t                *maxSigSize)
+{
+       OSStatus ortn = noErr;
+       assert(privKey != NULL);
+       assert(privKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY);
+       switch(privKey->KeyHeader.AlgorithmId) {
+               case CSSM_ALGID_RSA:
+                       *maxSigSize = sslPrivKeyLengthInBytes(privKey);
+                       break;
+               case CSSM_ALGID_DSA:
+               {
+                       /* DSA sig is DER sequence of two 160-bit integers */
+                       uint32_t sizeOfOneInt;
+                       sizeOfOneInt = (160 / 8) +      // the raw contents
+                                                       1 +                     // possible leading zero
+                                                       2;                      // tag + length (assume DER, not BER)
+                       *maxSigSize = (2 * sizeOfOneInt) + 5;
+                       break;
+               }
+               default:
+                       ortn = errSSLBadConfiguration;
+                       break;
+       }
+       return ortn;
+}
+/*
+ * Get raw key bits from an RSA public key.
+ */
+OSStatus sslGetPubKeyBits(
+       SSLContext                      *ctx,
+       const CSSM_KEY          *pubKey,
+       CSSM_CSP_HANDLE         cspHand,
+       SSLBuffer                       *modulus,               // data mallocd and RETURNED
+       SSLBuffer                       *exponent)              // data mallocd and RETURNED
+{
+       CSSM_KEY                        wrappedKey;
+       CSSM_BOOL                       didWrap = CSSM_FALSE;
+       const CSSM_KEYHEADER *hdr;
+       SSLBuffer                       pubKeyBlob;
+       OSStatus                        srtn;
+
+       assert(ctx != NULL);
+       assert(modulus != NULL);
+       assert(exponent != NULL);
+       assert(pubKey != NULL);
+
+       hdr = &pubKey->KeyHeader;
+       if(hdr->KeyClass != CSSM_KEYCLASS_PUBLIC_KEY) {
+               sslErrorLog("sslGetPubKeyBits: bad keyClass (%ld)\n", (long)hdr->KeyClass);
+               return errSSLInternal;
+       }
+       if(hdr->AlgorithmId != CSSM_ALGID_RSA) {
+               sslErrorLog("sslGetPubKeyBits: bad AlgorithmId (%ld)\n", (long)hdr->AlgorithmId);
+               return errSSLInternal;
+       }
+
+       /* Note currently ALL public keys are raw, obtained from the CL... */
+       assert(hdr->BlobType == CSSM_KEYBLOB_RAW);
+
+       /*
+        * Handle possible reference format - I think it should be in
+        * blob form since it came from the DL, but conversion is
+        * simple.
+        */
+       switch(hdr->BlobType) {
+               case CSSM_KEYBLOB_RAW:
+                       /* easy case */
+                       CSSM_TO_SSLBUF(&pubKey->KeyData, &pubKeyBlob);
+                       break;
+
+               case CSSM_KEYBLOB_REFERENCE:
+
+                       sslErrorLog("sslGetPubKeyBits: bad BlobType (%ld)\n",
+                               (long)hdr->BlobType);
+                       return errSSLInternal;
+
+                       #if 0
+                       /*
+                        * Convert to a blob via "NULL wrap"; no wrapping key,
+                        * ALGID_NONE
+                        */
+                       srtn = attachToCsp(ctx);
+                       if(srtn) {
+                               return srtn;
+                       }
+                       memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+                       crtn = CSSM_CSP_CreateSymmetricContext(ctx->cspHand,
+                                       CSSM_ALGID_NONE,
+                                       CSSM_ALGMODE_NONE,
+                                       &creds,                 // creds
+                                       pubKey,
+                                       NULL,                   // InitVector
+                                       CSSM_PADDING_NONE,
+                                       0,                      // reserved
+                                       &ccHand);
+                       if(crtn) {
+                               stPrintCdsaError("sslGetPubKeyBits: CreateSymmetricContext failure", crtn);
+                               return errSSLCrypto;
+                       }
+                       memset(&wrappedKey, 0, sizeof(CSSM_KEY));
+                       crtn = CSSM_WrapKey(ccHand,
+                               &creds,
+                               pubKey,
+                               NULL,                   // descriptiveData
+                               &wrappedKey);
+                       CSSM_DeleteContext(ccHand);
+                       if(crtn) {
+                               stPrintCdsaError("CSSM_WrapKey", crtn);
+                               return errSSLCrypto;
+                       }
+                       hdr = &wrappedKey.KeyHeader;
+                       if(hdr->BlobType != CSSM_KEYBLOB_RAW) {
+                               sslErrorLog("sslGetPubKeyBits: bad BlobType (%ld) after WrapKey\n",
+                                       hdr->BlobType);
+                               return errSSLCrypto;
+                       }
+                       didWrap = CSSM_TRUE;
+                       CSSM_TO_SSLBUF(&wrappedKey.KeyData, &pubKeyBlob);
+                       break;
+                       #endif  /* 0 */
+
+               default:
+                       sslErrorLog("sslGetPubKeyBits: bad BlobType (%ld)\n",
+                               (long)hdr->BlobType);
+                       return errSSLInternal;
+
+       }       /* switch BlobType */
+
+       assert(hdr->BlobType == CSSM_KEYBLOB_RAW);
+       srtn = sslDecodeRsaBlob(&pubKeyBlob, modulus, exponent);
+       if(didWrap) {
+               CSSM_FreeKey(ctx->cspHand, NULL, &wrappedKey, CSSM_FALSE);
+       }
+       return srtn;
+}
+
+/*
+ * Given raw RSA key bits, cook up a CSSM_KEY_PTR. Used in
+ * Server-initiated key exchange.
+ */
+OSStatus sslGetPubKeyFromBits(
+       SSLContext                      *ctx,
+       const SSLBuffer         *modulus,
+       const SSLBuffer         *exponent,
+       CSSM_KEY_PTR            *pubKey,                // mallocd and RETURNED
+       CSSM_CSP_HANDLE         *cspHand)               // RETURNED
+{
+       CSSM_KEY_PTR            key = NULL;
+       OSStatus                                serr;
+       SSLBuffer                       blob;
+       CSSM_KEYHEADER_PTR      hdr;
+       CSSM_KEY_SIZE           keySize;
+       CSSM_RETURN                     crtn;
+
+       assert((ctx != NULL) && (modulus != NULL) && (exponent != NULL));
+       assert((pubKey != NULL) && (cspHand != NULL));
+
+       *pubKey = NULL;
+       *cspHand = 0;
+
+       serr = attachToCsp(ctx);
+       if(serr) {
+               return serr;
+       }
+       serr = sslEncodeRsaBlob(modulus, exponent, &blob);
+       if(serr) {
+               return serr;
+       }
+
+       /* the rest is boilerplate, cook up a good-looking public key */
+       key = (CSSM_KEY_PTR)sslMalloc(sizeof(CSSM_KEY));
+       if(key == NULL) {
+               return memFullErr;
+       }
+       memset(key, 0, sizeof(CSSM_KEY));
+       hdr = &key->KeyHeader;
+
+    hdr->HeaderVersion = CSSM_KEYHEADER_VERSION;
+    /* key_ptr->KeyHeader.CspId is unknown (remains 0) */
+    hdr->BlobType = CSSM_KEYBLOB_RAW;
+    hdr->AlgorithmId = CSSM_ALGID_RSA;
+    hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
+    hdr->KeyClass = CSSM_KEYCLASS_PUBLIC_KEY;
+    /* comply with ASA requirements */
+    hdr->KeyUsage = CSSM_KEYUSE_VERIFY;
+    hdr->KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
+    /* key_ptr->KeyHeader.StartDate is unknown  (remains 0) */
+    /* key_ptr->KeyHeader.EndDate is unknown  (remains 0) */
+    hdr->WrapAlgorithmId = CSSM_ALGID_NONE;
+    hdr->WrapMode = CSSM_ALGMODE_NONE;
+
+       /* blob->data was mallocd by sslEncodeRsaBlob, pass it over to
+        * actual key */
+       SSLBUF_TO_CSSM(&blob, &key->KeyData);
+
+       /*
+        * Get keySizeInBits. This also serves to validate the key blob
+        * we just cooked up.
+        */
+    crtn = CSSM_QueryKeySizeInBits(ctx->cspHand, CSSM_INVALID_HANDLE, key, &keySize);
+       if(crtn) {
+       stPrintCdsaError("sslGetPubKeyFromBits: QueryKeySizeInBits\n", crtn);
+               serr = errSSLCrypto;
+       goto abort;
+       }
+
+       /* success */
+    hdr->LogicalKeySizeInBits = keySize.EffectiveKeySizeInBits;
+    *pubKey = key;
+    *cspHand = ctx->cspHand;
+       return noErr;
+
+abort:
+       /* note this frees the blob */
+       sslFreeKey(ctx->cspHand, &key, NULL);
+       return serr;
+}
+
+#ifdef UNUSED_FUNCTIONS
+/*
+ * NULL-unwrap a raw key to a ref key. Caller must free the returned key.
+ */
+static OSStatus sslNullUnwrapKey(
+       CSSM_CSP_HANDLE cspHand,
+       CSSM_KEY_PTR rawKey,
+       CSSM_KEY_PTR refKey)
+{
+       CSSM_DATA descData = {0, 0};
+       CSSM_RETURN crtn;
+       CSSM_CC_HANDLE ccHand;
+       CSSM_ACCESS_CREDENTIALS creds;
+       CSSM_DATA labelData = {4, (uint8 *)"none"};
+       uint32 keyAttr;
+       OSStatus ortn = noErr;
+
+       memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+       memset(refKey, 0, sizeof(CSSM_KEY));
+
+       crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
+                       CSSM_ALGID_NONE,
+                       CSSM_ALGMODE_NONE,
+                       &creds,
+                       NULL,                   // unwrappingKey
+                       NULL,                   // initVector
+                       CSSM_PADDING_NONE,
+                       0,                              // Params
+                       &ccHand);
+       if(crtn) {
+       stPrintCdsaError("sslNullUnwrapKey: CSSM_CSP_CreateSymmetricContext\n", crtn);
+               return errSSLCrypto;
+       }
+
+       keyAttr = rawKey->KeyHeader.KeyAttr;
+       keyAttr &= ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE |
+                                CSSM_KEYATTR_MODIFIABLE);
+       keyAttr |= CSSM_KEYATTR_RETURN_REF;
+       if(rawKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY) {
+               /* CSP policy */
+               keyAttr |= CSSM_KEYATTR_EXTRACTABLE;
+       }
+       crtn = CSSM_UnwrapKey(ccHand,
+               NULL,                           // PublicKey
+               rawKey,
+               rawKey->KeyHeader.KeyUsage,
+               keyAttr,
+               &labelData,
+               NULL,                           // CredAndAclEntry
+               refKey,
+               &descData);                     // required
+       if(crtn != CSSM_OK) {
+       stPrintCdsaError("sslNullUnwrapKey: CSSM_UnwrapKey\n", crtn);
+               ortn = errSSLCrypto;
+       }
+       if(CSSM_DeleteContext(ccHand)) {
+               printf("CSSM_DeleteContext failure\n");
+       }
+       return crtn;
+}
+#endif
+
+/*
+ * NULL-wrap a ref key to a raw key. Caller must free the returned key.
+ */
+static OSStatus sslNullWrapKey(
+       CSSM_CSP_HANDLE cspHand,
+       CSSM_KEY_PTR refKey,
+       CSSM_KEY_PTR rawKey)
+{
+       CSSM_DATA descData = {0, 0};
+       CSSM_RETURN crtn;
+       CSSM_CC_HANDLE ccHand;
+       CSSM_ACCESS_CREDENTIALS creds;
+       uint32 keyAttr;
+       OSStatus ortn = noErr;
+
+       memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+       memset(rawKey, 0, sizeof(CSSM_KEY));
+
+       crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
+                       CSSM_ALGID_NONE,
+                       CSSM_ALGMODE_NONE,
+                       &creds,
+                       NULL,                   // unwrappingKey
+                       NULL,                   // initVector
+                       CSSM_PADDING_NONE,
+                       0,                              // Params
+                       &ccHand);
+       if(crtn) {
+       stPrintCdsaError("sslNullWrapKey: CSSM_CSP_CreateSymmetricContext\n", crtn);
+               return errSSLCrypto;
+       }
+
+       keyAttr = rawKey->KeyHeader.KeyAttr;
+       keyAttr &= ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE |
+                                CSSM_KEYATTR_MODIFIABLE);
+       keyAttr |= CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
+       crtn = CSSM_WrapKey(ccHand,
+               &creds,
+               refKey,
+               &descData,
+               rawKey);
+       if(crtn != CSSM_OK) {
+       stPrintCdsaError("sslNullWrapKey: CSSM_WrapKey\n", crtn);
+               ortn = errSSLCrypto;
+       }
+       if(CSSM_DeleteContext(ccHand)) {
+               printf("CSSM_DeleteContext failure\n");
+       }
+       return crtn;
+}
+
+#pragma mark -
+#pragma mark Public Certificate Functions
+
+/*
+ * Given a DER-encoded cert, obtain its public key as a CSSM_KEY_PTR.
+ * Caller must CSSM_FreeKey and free the CSSM_KEY_PTR itself.
+ *
+ * For now, the returned cspHand is a copy of ctx->cspHand, so it
+ * doesn't have to be detached later - this may change.
+ *
+ * Update: since CSSM_CL_CertGetKeyInfo() doesn't provide a means for
+ * us to tell the CL what CSP to use, we really have no way of knowing
+ * what is going on here...we return the process-wide (bare) cspHand,
+ * which is currently always able to deal with this raw public key.
+ */
+OSStatus sslPubKeyFromCert(
+       SSLContext                      *ctx,
+       const SSLBuffer         *derCert,
+       SSLPubKey           *pubKey)            // RETURNED
+{
+       OSStatus                        serr;
+       CSSM_DATA               certData;
+       CSSM_RETURN             crtn;
+
+       assert(ctx != NULL);
+       assert(pubKey != NULL);
+
+       pubKey->key = NULL;
+       pubKey->cspHand = 0;
+
+       serr = attachToCl(ctx);
+       if(serr) {
+               return serr;
+       }
+       serr = attachToCsp(ctx);
+       if(serr) {
+               return serr;
+       }
+       SSLBUF_TO_CSSM(derCert, &certData);
+       crtn = CSSM_CL_CertGetKeyInfo(ctx->clHand, &certData, &pubKey->key);
+       if(crtn) {
+               return errSSLBadCert;
+       }
+       else {
+               pubKey->cspHand = ctx->cspHand;
+               return noErr;
+       }
+}
+
+/*
+ * Release each element in a CFArray.
+ */
+static void sslReleaseArray(
+       CFArrayRef a)
+{
+       CFIndex num = CFArrayGetCount(a);
+       CFIndex dex;
+       for(dex=0; dex<num; dex++) {
+               CFTypeRef elmt = (CFTypeRef)CFArrayGetValueAtIndex(a, dex);
+               secdebug("sslcert", "Freeing cert %p", elmt);
+               CFRelease(elmt);
+       }
+}
+
+/*
+ * Verify a chain of DER-encoded certs.
+ * Last cert in the chain is leaf.
+ *
+ * If arePeerCerts is true, host name verification is enabled and we
+ * save the resulting SecTrustRef in ctx->peerSecTrust. Otherwise
+ * we're just validating our own certs; no host name checking and
+ * peerSecTrust is transient.
+ */
+ OSStatus sslVerifyCertChain(
+       SSLContext                              *ctx,
+       const SSLCertificate    *certChain,
+       bool                                    arePeerCerts)
+{
+       uint32_t                                        numCerts;
+       int                                             i;
+       OSStatus                                        serr;
+       SSLCertificate                          *c = (SSLCertificate *)certChain;
+       CSSM_RETURN                                     crtn;
+       CSSM_APPLE_TP_SSL_OPTIONS       sslOpts;
+       CSSM_APPLE_TP_ACTION_DATA       tpActionData;
+       SecPolicyRef                            policy = NULL;
+       SecPolicySearchRef                      policySearch = NULL;
+       CFDataRef                                       actionData = NULL;
+       CSSM_DATA                                       sslOptsData;
+       CFMutableArrayRef                       anchors = NULL;
+       SecCertificateRef                       cert;                   // only lives in CFArrayRefs
+       SecTrustResultType                      secTrustResult;
+       CFMutableArrayRef                       kcList = NULL;
+       SecTrustRef                                     theTrust = NULL;
+
+       if(ctx->peerSecTrust && arePeerCerts) {
+               /* renegotiate - start with a new SecTrustRef */
+               CFRelease(ctx->peerSecTrust);
+               ctx->peerSecTrust = NULL;
+       }
+
+       numCerts = SSLGetCertificateChainLength(certChain);
+       if(numCerts == 0) {
+               /* nope */
+               return errSSLBadCert;
+       }
+
+       /*
+        * SSLCertificate chain --> CFArrayRef of SecCertificateRefs.
+        * TP Cert group has root at the end, opposite of
+        * SSLCertificate chain.
+        */
+       CFMutableArrayRef certGroup = CFArrayCreateMutable(NULL, numCerts,
+               &kCFTypeArrayCallBacks);
+       if(certGroup == NULL) {
+               return memFullErr;
+       }
+       /* subsequent errors to errOut: */
+
+       for(i=numCerts-1; i>=0; i--) {
+               CSSM_DATA cdata;
+               SSLBUF_TO_CSSM(&c->derCert, &cdata);
+               serr = SecCertificateCreateFromData(&cdata,     CSSM_CERT_X_509v3,
+                       CSSM_CERT_ENCODING_DER, &cert);
+               if(serr) {
+                       goto errOut;
+               }
+               /*
+                * Can't set a value at index i when there is an empty element
+                * at i=1!
+                */
+               secdebug("sslcert", "Adding cert %p", cert);
+               CFArrayInsertValueAtIndex(certGroup, 0, cert);
+               c = c->next;
+       }
+
+       /*
+        * Cook up an SSL-specific SecPolicyRef. This will persists as part
+        * of the SecTrustRef object we'll be creating.
+        */
+       serr = SecPolicySearchCreate(CSSM_CERT_X_509v3,
+               &CSSMOID_APPLE_TP_SSL,
+               NULL,
+               &policySearch);
+       if(serr) {
+               sslErrorLog("***sslVerifyCertChain: SecPolicySearchCreate rtn %d\n",
+                       (int)serr);
+               goto errOut;
+       }
+       serr = SecPolicySearchCopyNext(policySearch, &policy);
+       if(serr) {
+               sslErrorLog("***sslVerifyCertChain: SecPolicySearchCopyNext rtn %d\n",
+                       (int)serr);
+               goto errOut;
+       }
+       sslOpts.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
+       if(arePeerCerts) {
+               sslOpts.ServerNameLen = ctx->peerDomainNameLen;
+               sslOpts.ServerName = ctx->peerDomainName;
+       }
+       else {
+               sslOpts.ServerNameLen = 0;
+               sslOpts.ServerName = NULL;
+       }
+       sslOpts.Flags = 0;
+       if(ctx->protocolSide == kSSLServerSide) {
+               /* we're evaluating a client cert */
+               sslOpts.Flags |= CSSM_APPLE_TP_SSL_CLIENT;
+       }
+       sslOptsData.Data = (uint8 *)&sslOpts;
+       sslOptsData.Length = sizeof(sslOpts);
+       serr = SecPolicySetValue(policy, &sslOptsData);
+       if(serr) {
+               sslErrorLog("***sslVerifyCertChain: SecPolicySetValue rtn %d\n",
+                       (int)serr);
+               goto errOut;
+       }
+
+       /* now a SecTrustRef */
+       serr = SecTrustCreateWithCertificates(certGroup, policy, &theTrust);
+       if(serr) {
+               sslErrorLog("***sslVerifyCertChain: SecTrustCreateWithCertificates "
+                       "rtn %d\n",     (int)serr);
+               goto errOut;
+       }
+
+       /* anchors - default, or ours? */
+       if(ctx->trustedCerts != NULL) {
+               serr = SecTrustSetAnchorCertificates(theTrust, ctx->trustedCerts);
+               if(serr) {
+                       sslErrorLog("***sslVerifyCertChain: SecTrustSetAnchorCertificates "
+                               "rtn %d\n",     (int)serr);
+                       goto errOut;
+               }
+       }
+       tpActionData.Version = CSSM_APPLE_TP_ACTION_VERSION;
+       tpActionData.ActionFlags = 0;
+       if(ctx->allowExpiredCerts) {
+               tpActionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED;
+       }
+       if(ctx->allowExpiredRoots) {
+               tpActionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT;
+       }
+       actionData = CFDataCreate(NULL, (UInt8 *)&tpActionData, sizeof(tpActionData));
+
+       serr = SecTrustSetParameters(theTrust, CSSM_TP_ACTION_DEFAULT,
+               actionData);
+       if(serr) {
+               sslErrorLog("***sslVerifyCertChain: SecTrustSetParameters rtn %d\n",
+                       (int)serr);
+               goto errOut;
+       }
+
+       #if 0
+       /* Disabled for Radar 3421314 */
+       /*
+        * Avoid searching user keychains for intermediate certs by specifying
+        * an empty array of keychains
+        */
+       kcList = CFArrayCreateMutable(NULL, 0, NULL);
+       if(kcList == NULL) {
+               sslErrorLog("***sslVerifyCertChain: error creating null kcList\n");
+               serr = memFullErr;
+               goto errOut;
+       }
+       serr = SecTrustSetKeychains(theTrust, kcList);
+       if(serr) {
+               sslErrorLog("***sslVerifyCertChain: SecTrustSetKeychains rtn %d\n",
+                       (int)serr);
+               goto errOut;
+       }
+       #endif
+
+       /*
+        * Save this no matter what if we're evaluating peer certs.
+        * We do a retain here so we can unconditionally release theTrust
+        * at the end of this routine in case of previous error or
+        * !arePeerCerts.
+        */
+       if(arePeerCerts) {
+               ctx->peerSecTrust = theTrust;
+               CFRetain(theTrust);
+       }
+
+       if(!ctx->enableCertVerify) {
+               /* trivial case, this is caller's responsibility */
+               serr = noErr;
+               goto errOut;
+       }
+
+    /*
+     * If the caller provided a list of trusted leaf certs, check them here
+     */
+       if(ctx->trustedLeafCerts) {
+               if (sslGetMatchingCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(certGroup, 0),
+                       ctx->trustedLeafCerts)) {
+                       serr = noErr;
+                       goto errOut;
+               }
+       }
+
+       /*
+        * Here we go; hand it over to SecTrust/TP.
+        */
+       serr = SecTrustEvaluate(theTrust, &secTrustResult);
+       if(serr) {
+               sslErrorLog("***sslVerifyCertChain: SecTrustEvaluate rtn %d\n",
+                       (int)serr);
+               goto errOut;
+       }
+       switch(secTrustResult) {
+               case kSecTrustResultUnspecified:
+                       /* cert chain valid, no special UserTrust assignments */
+               case kSecTrustResultProceed:
+                       /* cert chain valid AND user explicitly trusts this */
+                       crtn = CSSM_OK;
+                       break;
+               case kSecTrustResultDeny:
+               case kSecTrustResultConfirm:
+                       /*
+                        * Cert chain may well have verified OK, but user has flagged
+                        * one of these certs as untrustable.
+                        */
+                       crtn = CSSMERR_TP_NOT_TRUSTED;
+                       break;
+               default:
+               {
+                       OSStatus osCrtn;
+                       serr = SecTrustGetCssmResultCode(theTrust, &osCrtn);
+                       if(serr) {
+                               sslErrorLog("***sslVerifyCertChain: SecTrustGetCssmResultCode"
+                                       " rtn %d\n", (int)serr);
+                               goto errOut;
+                       }
+                       crtn = osCrtn;
+               }
+       }
+       if(crtn) {
+               /* get some detailed error info */
+               switch(crtn) {
+                       case CSSMERR_TP_INVALID_ANCHOR_CERT:
+                               /* root found but we don't trust it */
+                               if(ctx->allowAnyRoot) {
+                                       serr = noErr;
+                                       sslErrorLog("***Warning: accepting unknown root cert\n");
+                               }
+                               else {
+                                       serr = errSSLUnknownRootCert;
+                               }
+                               break;
+                       case CSSMERR_TP_NOT_TRUSTED:
+                               /* no root, not even in implicit SSL roots */
+                               if(ctx->allowAnyRoot) {
+                                       sslErrorLog("***Warning: accepting unverified cert chain\n");
+                                       serr = noErr;
+                               }
+                               else {
+                                       serr = errSSLNoRootCert;
+                               }
+                               break;
+                       case CSSMERR_TP_CERT_EXPIRED:
+                               assert(!ctx->allowExpiredCerts);
+                               serr = errSSLCertExpired;
+                               break;
+                       case CSSMERR_TP_CERT_NOT_VALID_YET:
+                               serr = errSSLCertNotYetValid;
+                               break;
+                       case CSSMERR_APPLETP_HOSTNAME_MISMATCH:
+                               serr = errSSLHostNameMismatch;
+                               break;
+                       case errSecInvalidTrustSettings:
+                               /* these get passed along unmodified */
+                               serr = crtn;
+                               break;
+                       default:
+                               stPrintCdsaError("sslVerifyCertChain: SecTrustEvaluate returned",
+                                               crtn);
+                               serr = errSSLXCertChainInvalid;
+                               break;
+               }
+       }       /* SecTrustEvaluate error */
+
+errOut:
+       /*
+        * Free up resources - certGroup, policy, etc. Note that most of these
+        * will actually persist as long as the current SSLContext does since
+        * peerSecTrust holds references to these.
+        */
+       if(policy) {
+               CFRelease(policy);
+       }
+       if(policySearch) {
+               CFRelease(policySearch);
+       }
+       if(actionData) {
+               CFRelease(actionData);
+       }
+       if(anchors) {
+               sslReleaseArray(anchors);
+               CFRelease(anchors);
+       }
+       if(certGroup) {
+               sslReleaseArray(certGroup);
+               CFRelease(certGroup);
+       }
+       if(kcList) {
+               /* empty, no contents to release */
+               CFRelease(kcList);
+       }
+       if(theTrust) {
+               CFRelease(theTrust);
+       }
+       return serr;
+}
+
+#ifndef        NDEBUG
+void stPrintCdsaError(const char *op, CSSM_RETURN crtn)
+{
+       cssmPerror(op, crtn);
+}
+#endif
+
+#pragma mark -
+#pragma mark Diffie-Hellman Support
+
+/*
+ * Generate a Diffie-Hellman key pair. Algorithm parameters always
+ * come from the server, so on client side we have the parameters
+ * as two SSLBuffers. On server side we have the pre-encoded block
+ * which comes from ServerDhParams.
+ */
+OSStatus sslDhGenKeyPairClient(
+       SSLContext              *ctx,
+       const SSLBuffer *prime,
+       const SSLBuffer *generator,
+       CSSM_KEY_PTR    publicKey,              // RETURNED
+       CSSM_KEY_PTR    privateKey)             // RETURNED
+{
+       assert((prime->data != NULL) && (generator->data != NULL));
+       if(prime->data && !generator->data) {
+               return errSSLProtocol;
+       }
+       if(!prime->data && generator->data) {
+               return errSSLProtocol;
+       }
+
+       SSLBuffer sParam;
+       OSStatus ortn = sslEncodeDhParams(prime, generator, &sParam);
+       if(ortn) {
+               sslErrorLog("***sslDhGenerateKeyPairClient: DH param error\n");
+               return ortn;
+       }
+       ortn = sslDhGenerateKeyPair(ctx, &sParam, prime->length * 8, publicKey, privateKey);
+       SSLFreeBuffer(&sParam, ctx);
+       return ortn;
+}
+
+OSStatus sslDhGenerateKeyPair(
+       SSLContext              *ctx,
+       const SSLBuffer *paramBlob,
+       uint32_t                keySizeInBits,
+       CSSM_KEY_PTR    publicKey,              // RETURNED
+       CSSM_KEY_PTR    privateKey)             // RETURNED
+{
+       CSSM_RETURN             crtn;
+       CSSM_CC_HANDLE  ccHandle;
+       CSSM_DATA               labelData = {8, (uint8 *)"tempKey"};
+       OSStatus                ortn = noErr;
+       CSSM_DATA               cParamBlob;
+
+       assert(ctx != NULL);
+       assert(ctx->cspHand != 0);
+
+       memset(publicKey, 0, sizeof(CSSM_KEY));
+       memset(privateKey, 0, sizeof(CSSM_KEY));
+       SSLBUF_TO_CSSM(paramBlob, &cParamBlob);
+
+       crtn = CSSM_CSP_CreateKeyGenContext(ctx->cspHand,
+               CSSM_ALGID_DH,
+               keySizeInBits,
+               NULL,                                   // Seed
+               NULL,                                   // Salt
+               NULL,                                   // StartDate
+               NULL,                                   // EndDate
+               &cParamBlob,
+               &ccHandle);
+       if(crtn) {
+               stPrintCdsaError("DH CSSM_CSP_CreateKeyGenContext", crtn);
+               return errSSLCrypto;
+       }
+
+       crtn = CSSM_GenerateKeyPair(ccHandle,
+               CSSM_KEYUSE_DERIVE,             // only legal use of a Diffie-Hellman key
+               CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
+               &labelData,
+               publicKey,
+               /* private key specification */
+               CSSM_KEYUSE_DERIVE,
+               CSSM_KEYATTR_RETURN_REF,
+               &labelData,                             // same labels
+               NULL,                                   // CredAndAclEntry
+               privateKey);
+       if(crtn) {
+               stPrintCdsaError("DH CSSM_GenerateKeyPair", crtn);
+               ortn = errSSLCrypto;
+       }
+       CSSM_DeleteContext(ccHandle);
+       return ortn;
+}
+
+/*
+ * Perform Diffie-Hellman key exchange.
+ * Valid on entry:
+ *     ctx->dhPrivate
+ *             ctx->dhPeerPublic
+ *
+ * This generates deriveSizeInBits of key-exchanged data.
+ */
+
+/* the alg isn't important; we just want to be able to cook up lots of bits */
+#define DERIVE_KEY_ALG                 CSSM_ALGID_RC5
+
+OSStatus sslDhKeyExchange(
+       SSLContext              *ctx,
+       uint32_t                deriveSizeInBits,
+       SSLBuffer               *exchanged)
+{
+       CSSM_RETURN                     crtn;
+       CSSM_ACCESS_CREDENTIALS creds;
+       CSSM_CC_HANDLE                  ccHandle;
+       CSSM_DATA                               labelData = {8, (uint8 *)"tempKey"};
+       CSSM_KEY                                derivedKey;
+       OSStatus                                ortn = noErr;
+
+       assert(ctx != NULL);
+       assert(ctx->cspHand != 0);
+       assert(ctx->dhPrivate != NULL);
+       if(ctx->dhPeerPublic.length == 0) {
+               /* comes from peer, don't panic */
+               sslErrorLog("cdsaDhKeyExchange: null peer public key\n");
+               return errSSLProtocol;
+       }
+
+       memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+       memset(&derivedKey, 0, sizeof(CSSM_KEY));
+
+       crtn = CSSM_CSP_CreateDeriveKeyContext(ctx->cspHand,
+               CSSM_ALGID_DH,
+               DERIVE_KEY_ALG,
+               deriveSizeInBits,
+               &creds,
+               ctx->dhPrivate, // BaseKey
+               0,                              // IterationCount
+               0,                              // Salt
+               0,                              // Seed
+               &ccHandle);
+       if(crtn) {
+               stPrintCdsaError("DH CSSM_CSP_CreateDeriveKeyContext", crtn);
+               return errSSLCrypto;
+       }
+
+       /* public key passed in as CSSM_DATA *Param */
+       CSSM_DATA theirPubKeyData;
+       SSLBUF_TO_CSSM(&ctx->dhPeerPublic, &theirPubKeyData);
+
+       crtn = CSSM_DeriveKey(ccHandle,
+               &theirPubKeyData,
+               CSSM_KEYUSE_ANY,
+               CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
+               &labelData,
+               NULL,                           // cread/acl
+               &derivedKey);
+       if(crtn) {
+               stPrintCdsaError("DH CSSM_DeriveKey", crtn);
+               ortn = errSSLCrypto;
+       }
+       else {
+               CSSM_TO_SSLBUF(&derivedKey.KeyData, exchanged);
+       }
+       CSSM_DeleteContext(ccHandle);
+       return ortn;
+}
+
+#pragma mark -
+#pragma mark *** ECDSA support ***
+
+/* specify either 32-bit integer or a pointer as an added attribute value */
+typedef enum {
+       CAT_Uint32,
+       CAT_Ptr
+} ContextAttrType;
+
+/*
+ * Given a context specified via a CSSM_CC_HANDLE, add a new
+ * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
+ * AttributeLength, and an untyped pointer.
+ */
+static CSSM_RETURN sslAddContextAttribute(CSSM_CC_HANDLE CCHandle,
+       uint32 AttributeType,
+       uint32 AttributeLength,
+       ContextAttrType attrType,
+       /* specify exactly one of these */
+       const void *AttributePtr,
+       uint32 attributeInt)
+{
+       CSSM_CONTEXT_ATTRIBUTE          newAttr;
+       CSSM_RETURN                                     crtn;
+
+       newAttr.AttributeType     = AttributeType;
+       newAttr.AttributeLength   = AttributeLength;
+       if(attrType == CAT_Uint32) {
+               newAttr.Attribute.Uint32  = attributeInt;
+       }
+       else {
+               newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
+       }
+       crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
+       if(crtn) {
+               stPrintCdsaError("CSSM_UpdateContextAttributes", crtn);
+       }
+       return crtn;
+}
+
+/*
+ * Generate ECDH key pair with the given SSL_ECDSA_NamedCurve.
+ * Private key, in ref form, is placed in ctx->ecdhPrivate.
+ * Public key, in ECPoint form - which can NOT be used as
+ * a key in any CSP ops - is placed in ecdhExchangePublic.
+ */
+OSStatus sslEcdhGenerateKeyPair(
+       SSLContext *ctx,
+       SSL_ECDSA_NamedCurve namedCurve)
+{
+       CSSM_RETURN             crtn;
+       CSSM_CC_HANDLE  ccHandle = 0;
+       CSSM_DATA               labelData = {8, (uint8 *)"ecdsaKey"};
+       OSStatus                ortn = noErr;
+       CSSM_KEY                pubKey;
+       uint32                  keySizeInBits;
+
+       assert(ctx != NULL);
+       assert(ctx->cspHand != 0);
+       sslFreeKey(ctx->ecdhPrivCspHand, &ctx->ecdhPrivate, NULL);
+    SSLFreeBuffer(&ctx->ecdhExchangePublic, ctx);
+
+       switch(namedCurve) {
+               case SSL_Curve_secp256r1:
+                       keySizeInBits = 256;
+                       break;
+               case SSL_Curve_secp384r1:
+                       keySizeInBits = 384;
+                       break;
+               case SSL_Curve_secp521r1:
+                       keySizeInBits = 521;
+                       break;
+               default:
+                       /* should not have gotten this far */
+                       sslErrorLog("sslEcdhGenerateKeyPair: bad namedCurve (%u)\n",
+                               (unsigned)namedCurve);
+                       return errSSLInternal;
+       }
+
+       ctx->ecdhPrivate = (CSSM_KEY *)sslMalloc(sizeof(CSSM_KEY));
+
+       memset(ctx->ecdhPrivate, 0, sizeof(CSSM_KEY));
+       memset(&pubKey, 0, sizeof(CSSM_KEY));
+
+       crtn = CSSM_CSP_CreateKeyGenContext(ctx->cspHand,
+               CSSM_ALGID_ECDSA,
+               keySizeInBits,
+               NULL,                                   // Seed
+               NULL,                                   // Salt
+               NULL,                                   // StartDate
+               NULL,                                   // EndDate
+               NULL,                                   // Params
+               &ccHandle);
+       if(crtn) {
+               stPrintCdsaError("ECDH CSSM_CSP_CreateKeyGenContext", crtn);
+               return errSSLCrypto;
+       }
+       /* subsequent errors to errOut: */
+
+       /*
+        * Here's how we get the raw ECPoint form of a public key
+        */
+       crtn = sslAddContextAttribute(ccHandle,
+               CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
+               sizeof(uint32),
+               CAT_Uint32,
+               NULL,
+               CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING);
+       if(crtn) {
+               ortn = errSSLCrypto;
+               goto errOut;
+       }
+
+       crtn = CSSM_GenerateKeyPair(ccHandle,
+               /* public key specification */
+               CSSM_KEYUSE_DERIVE,             // only legal use - right?
+               CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
+               &labelData,
+               &pubKey,
+               /* private key specification */
+               CSSM_KEYUSE_DERIVE,
+               CSSM_KEYATTR_RETURN_REF,
+               &labelData,                             // same labels
+               NULL,                                   // CredAndAclEntry
+               ctx->ecdhPrivate);
+       if(crtn) {
+               stPrintCdsaError("ECDH CSSM_GenerateKeyPair", crtn);
+               ortn = errSSLCrypto;
+               goto errOut;
+       }
+       ctx->ecdhPrivCspHand = ctx->cspHand;
+
+       /*
+        * Take that public key data, drop it into ecdhExchangePublic,
+        * and free the key.
+        */
+    ortn = SSLCopyBufferFromData(pubKey.KeyData.Data, pubKey.KeyData.Length,
+                               &ctx->ecdhExchangePublic);
+       CSSM_FreeKey(ctx->cspHand, NULL, &pubKey, CSSM_FALSE);
+
+errOut:
+       if(ccHandle != 0) {
+               CSSM_DeleteContext(ccHandle);
+       }
+       return ortn;
+
+}
+
+/*
+ * Perform ECDH key exchange. Obtained key material is the same
+ * size as our private key.
+ *
+ * On entry, ecdhPrivate is our private key. The peer's public key
+ * is either ctx->ecdhPeerPublic for ECDHE exchange, or
+ * ctx->peerPubKey for ECDH exchange.
+ */
+OSStatus sslEcdhKeyExchange(
+       SSLContext              *ctx,
+       SSLBuffer               *exchanged)
+{
+       CSSM_RETURN                     crtn;
+       CSSM_ACCESS_CREDENTIALS creds;
+       const CSSM_ACCESS_CREDENTIALS *secCreds;
+       const CSSM_ACCESS_CREDENTIALS *useCreds = &creds;
+       CSSM_CC_HANDLE                  ccHandle;
+       CSSM_DATA                               labelData = {8, (uint8 *)"tempKey"};
+       CSSM_KEY                                derivedKey;
+       OSStatus                                ortn = noErr;
+       CSSM_KEY                                rawKey;
+       bool                                    useRefKeys = false;
+       uint32                                  keyAttr;
+       SSLBuffer                               pubKeyBits = {0, NULL};
+
+       assert(ctx != NULL);
+       assert(ctx->ecdhPrivCspHand != 0);
+       assert(ctx->ecdhPrivate != NULL);
+
+       memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+       memset(&derivedKey, 0, sizeof(CSSM_KEY));
+       memset(&rawKey, 0, sizeof(CSSM_KEY));
+
+       /*
+        * If we're using an actual CSSM_KEY for the peer public key, and its
+        * cspHand differs from our private key, we do things a bit different -
+        * our key is in the CSPDL space, and it has a Sec-style ACL for which
+        * we need creds. Also, the peer public key and the derived key have
+        * to be in reference form.
+        */
+       switch(ctx->selectedCipherSpec.keyExchangeMethod) {
+               case SSL_ECDH_ECDSA:
+               case SSL_ECDH_RSA:
+                       if(ctx->cspHand != ctx->ecdhPrivCspHand) {
+                               useRefKeys = true;
+                       }
+                       break;
+               default:
+                       break;
+       }
+       if(useRefKeys) {
+               assert(ctx->signingPrivKeyRef != NULL);
+               ortn = SecKeyGetCredentials(ctx->signingPrivKeyRef,
+                       CSSM_ACL_AUTHORIZATION_DERIVE,
+                       kSecCredentialTypeDefault,
+                       &secCreds);
+               if(ortn) {
+                       stPrintCdsaError("ECDH SecKeyGetCredentials", ortn);
+                       return ortn;
+               }
+               useCreds = secCreds;
+       }
+       crtn = CSSM_CSP_CreateDeriveKeyContext(ctx->ecdhPrivCspHand,
+               CSSM_ALGID_ECDH,
+               DERIVE_KEY_ALG,
+               ctx->ecdhPrivate->KeyHeader.LogicalKeySizeInBits,
+               useCreds,
+               ctx->ecdhPrivate,       // BaseKey
+               0,                                      // IterationCount
+               0,                                      // Salt
+               0,                                      // Seed
+               &ccHandle);
+       if(crtn) {
+               stPrintCdsaError("ECDH CSSM_CSP_CreateDeriveKeyContext", crtn);
+               return errSSLCrypto;
+       }
+       /* subsequent errors to errOut: */
+
+       CSSM_DATA theirPubKeyData = {0, NULL};
+
+       switch(ctx->selectedCipherSpec.keyExchangeMethod) {
+               case SSL_ECDHE_ECDSA:
+               case SSL_ECDHE_RSA:
+                       /* public key passed in as CSSM_DATA *Param */
+                       if(ctx->ecdhPeerPublic.length == 0) {
+                               /* comes from peer, don't panic */
+                               sslErrorLog("sslEcdhKeyExchange: null peer public key\n");
+                               ortn = errSSLProtocol;
+                               goto errOut;
+                       }
+                       SSLBUF_TO_CSSM(&ctx->ecdhPeerPublic, &theirPubKeyData);
+                       break;
+               case SSL_ECDH_ECDSA:
+               case SSL_ECDH_RSA:
+                       /* add pub key as a context attr */
+                       if(ctx->peerPubKey == NULL) {
+                          sslErrorLog("sslEcdhKeyExchange: no peer key\n");
+                          ortn = errSSLInternal;
+                          goto errOut;
+                       }
+
+                       /*
+                        * If we're using CSPDL, extract the raw public key bits in ECPoint
+                        * form and transmit as CSSM_DATA *Param.
+                        * The securityd can't transmit a public key in a DeriveKey context
+                        * in any form.
+                        */
+                       if(useRefKeys) {
+                               ortn = sslEcdsaPubKeyBits(ctx->peerPubKey, &pubKeyBits);
+                               if(ortn) {
+                                       goto errOut;
+                               }
+                               SSLBUF_TO_CSSM(&pubKeyBits, &theirPubKeyData);
+                       }
+                       else {
+                               crtn = sslAddContextAttribute(ccHandle,
+                                       CSSM_ATTRIBUTE_PUBLIC_KEY,
+                                       sizeof(CSSM_KEY),
+                                       CAT_Ptr,
+                                       (void *)ctx->peerPubKey,
+                                       0);
+                               if(crtn) {
+                                       stPrintCdsaError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY)",
+                                               crtn);
+                                       ortn = errSSLInternal;
+                                       goto errOut;
+                               }
+                       }
+                       break;
+               default:
+                       /* shouldn't be here */
+                       assert(0);
+                       ortn = errSSLInternal;
+                       goto errOut;
+       }
+
+       if(useRefKeys) {
+               keyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
+       }
+       else {
+               keyAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
+       }
+       crtn = CSSM_DeriveKey(ccHandle,
+               &theirPubKeyData,
+               CSSM_KEYUSE_ANY,
+               keyAttr,
+               &labelData,
+               NULL,                           // cred/acl
+               &derivedKey);
+       if(crtn) {
+               stPrintCdsaError("ECDH CSSM_DeriveKey", crtn);
+               ortn = errSSLCrypto;
+               goto errOut;
+       }
+
+       if(useRefKeys) {
+               /*
+                * one more step: NULL-wrap the generated ref key to something we
+                * can use
+                */
+               ortn = sslNullWrapKey(ctx->ecdhPrivCspHand, &derivedKey, &rawKey);
+               if(ortn) {
+                       goto errOut;
+               }
+               ortn = SSLCopyBufferFromData(rawKey.KeyData.Data, rawKey.KeyData.Length,
+                       exchanged);
+       }
+       else {
+               ortn = SSLCopyBufferFromData(derivedKey.KeyData.Data,
+                               derivedKey.KeyData.Length, exchanged);
+       }
+errOut:
+       CSSM_DeleteContext(ccHandle);
+       if(useRefKeys) {
+               if(pubKeyBits.length) {
+                       SSLFreeBuffer(&pubKeyBits, ctx);
+               }
+               if(rawKey.KeyData.Length) {
+                       CSSM_FreeKey(ctx->ecdhPrivCspHand, NULL, &rawKey, CSSM_FALSE);
+               }
+       }
+       return ortn;
+}
+
+
+/*
+ * After ciphersuite negotiation is complete, verify that we have
+ * the capability of actually performing the selected cipher.
+ * Currently we just verify that we have a cert and private signing
+ * key, if needed, and that the signing key's algorithm matches the
+ * expected key exchange method.
+ *
+ * This is currently called from FindCipherSpec(), after it sets
+ * ctx->selectedCipherSuite to a (supposedly) valid value, and from
+ * sslBuildCipherSuiteArray(), in server mode (pre-negotiation) only.
+ */
+OSStatus sslVerifySelectedCipher(
+       SSLContext *ctx,
+       const SSLCipherSpec *selectedCipherSpec)
+{
+       if(ctx->protocolSide == kSSLClientSide) {
+               return noErr;
+       }
+       #if     SSL_PAC_SERVER_ENABLE
+       if((ctx->masterSecretCallback != NULL) &&
+          (ctx->sessionTicket.data != NULL)) {
+               /* EAP via PAC resumption; we can do it */
+               return noErr;
+       }
+       #endif  /* SSL_PAC_SERVER_ENABLE */
+
+       CSSM_ALGORITHMS requireAlg = CSSM_ALGID_NONE;
+    if(selectedCipherSpec == NULL) {
+               return errSSLInternal;
+    }
+    switch (selectedCipherSpec->keyExchangeMethod) {
+               case SSL_RSA:
+        case SSL_RSA_EXPORT:
+               case SSL_DH_RSA:
+               case SSL_DH_RSA_EXPORT:
+               case SSL_DHE_RSA:
+               case SSL_DHE_RSA_EXPORT:
+                       requireAlg = CSSM_ALGID_RSA;
+                       break;
+               case SSL_DHE_DSS:
+               case SSL_DHE_DSS_EXPORT:
+               case SSL_DH_DSS:
+               case SSL_DH_DSS_EXPORT:
+                       requireAlg = CSSM_ALGID_DSA;
+                       break;
+               case SSL_DH_anon:
+               case SSL_DH_anon_EXPORT:
+                       /* CSSM_ALGID_NONE, no signing key */
+                       break;
+               /*
+                * When SSL_ECDSA_SERVER is true and we support ECDSA on the server side,
+                * we'll need to add some logic here...
+                */
+               #if SSL_ECDSA_SERVER
+               #error Work needed in sslVerifySelectedCipher
+               #endif
+
+               default:
+                       /* needs update per cipherSpecs.c */
+                       assert(0);
+                       return errSSLInternal;
+    }
+       if(requireAlg == CSSM_ALGID_NONE) {
+               return noErr;
+       }
+
+       /* private signing key required */
+       if(ctx->signingPrivKeyRef == NULL) {
+               sslErrorLog("sslVerifySelectedCipher: no signing key\n");
+               return errSSLBadConfiguration;
+       }
+       {
+               const CSSM_KEY *cssmKey;
+               OSStatus ortn = SecKeyGetCSSMKey(ctx->signingPrivKeyRef, &cssmKey);
+               if(ortn) {
+                       sslErrorLog("sslVerifySelectedCipher: SecKeyGetCSSMKey err %d\n",
+                       (int)ortn);
+                       return ortn;
+               }
+               if(cssmKey->KeyHeader.AlgorithmId != requireAlg) {
+                       sslErrorLog("sslVerifySelectedCipher: signing key alg mismatch\n");
+                       return errSSLBadConfiguration;
+               }
+       }
+       return noErr;
+}
+
+#endif /* USE_CDSA_CRYPTO */
+