]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_ssl/lib/sslKeyExchange.c
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_ssl / lib / sslKeyExchange.c
diff --git a/libsecurity_ssl/lib/sslKeyExchange.c b/libsecurity_ssl/lib/sslKeyExchange.c
new file mode 100644 (file)
index 0000000..a0e5549
--- /dev/null
@@ -0,0 +1,2008 @@
+/*
+ * Copyright (c) 1999-2001,2005-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@
+ */
+
+/*
+ * sslKeyExchange.c - Support for key exchange and server key exchange
+ */
+
+#include "ssl.h"
+#include "sslContext.h"
+#include "sslHandshake.h"
+#include "sslMemory.h"
+#include "sslDebug.h"
+#include "sslUtils.h"
+#include "sslCrypto.h"
+
+#include "sslDigests.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include <stdio.h>
+
+#include <corecrypto/ccdh_gp.h>
+
+#ifdef USE_CDSA_CRYPTO
+//#include <security_utilities/globalizer.h>
+//#include <security_utilities/threading.h>
+#include <Security/cssmapi.h>
+#include <Security/SecKeyPriv.h>
+#include "ModuleAttacher.h"
+#else
+#include <Security/SecRSAKey.h>
+#include <AssertMacros.h>
+#include <Security/oidsalg.h>
+#if APPLE_DH
+
+
+static OSStatus SSLGenServerDHParamsAndKey(SSLContext *ctx);
+static size_t SSLEncodedDHKeyParamsLen(SSLContext *ctx);
+static OSStatus SSLEncodeDHKeyParams(SSLContext *ctx, uint8_t *charPtr);
+
+#endif /* APPLE_DH */
+#endif /* USE_CDSA_CRYPTO */
+
+#include <pthread.h>
+
+#pragma mark -
+#pragma mark Forward Static Declarations
+
+#if APPLE_DH
+#if USE_CDSA_CRYPTO
+static OSStatus SSLGenServerDHParamsAndKey(SSLContext *ctx);
+static OSStatus SSLEncodeDHKeyParams(SSLContext *ctx, uint8_t *charPtr);
+#endif
+static OSStatus SSLDecodeDHKeyParams(SSLContext *ctx, uint8_t **charPtr,
+       size_t length);
+#endif
+static OSStatus SSLDecodeECDHKeyParams(SSLContext *ctx, uint8_t **charPtr,
+       size_t length);
+
+#define DH_PARAM_DUMP          0
+#if    DH_PARAM_DUMP
+
+static void dumpBuf(const char *name, SSLBuffer *buf)
+{
+       printf("%s:\n", name);
+       uint8_t *cp = buf->data;
+       uint8_t *endCp = cp + buf->length;
+
+       do {
+               unsigned i;
+               for(i=0; i<16; i++) {
+                       printf("%02x ", *cp++);
+                       if(cp == endCp) {
+                               break;
+                       }
+               }
+               if(cp == endCp) {
+                       break;
+               }
+               printf("\n");
+       } while(cp < endCp);
+       printf("\n");
+}
+#else
+#define dumpBuf(n, b)
+#endif /* DH_PARAM_DUMP */
+
+#if    APPLE_DH
+
+#pragma mark -
+#pragma mark Local Diffie-Hellman Parameter Generator
+
+/*
+ * Process-wide server-supplied Diffie-Hellman parameters.
+ * This might be overridden by some API_supplied parameters
+ * in the future.
+ */
+struct ServerDhParams
+{
+       /* these two for sending over the wire */
+       SSLBuffer               prime;
+       SSLBuffer               generator;
+       /* this one for sending to the CSP at key gen time */
+       SSLBuffer               paramBlock;
+};
+
+
+#endif /* APPLE_DH */
+
+#pragma mark -
+#pragma mark RSA Key Exchange
+
+/*
+ * Client RSA Key Exchange msgs actually start with a two-byte
+ * length field, contrary to the first version of RFC 2246, dated
+ * January 1999. See RFC 2246, March 2002, section 7.4.7.1 for
+ * updated requirements.
+ */
+#define RSA_CLIENT_KEY_ADD_LENGTH              1
+
+static OSStatus
+SSLEncodeRSAKeyParams(SSLBuffer *keyParams, SSLPubKey *key, SSLContext *ctx)
+{
+#if 0
+    SSLBuffer   modulus, exponent;
+    uint8_t     *charPtr;
+
+#ifdef USE_CDSA_CRYPTO
+       if(err = attachToCsp(ctx)) {
+               return err;
+       }
+
+       /* Note currently ALL public keys are raw, obtained from the CL... */
+       assert((*key)->KeyHeader.BlobType == CSSM_KEYBLOB_RAW);
+#endif /* USE_CDSA_CRYPTO */
+
+       err = sslGetPubKeyBits(ctx,
+               key,
+               &modulus,
+               &exponent);
+       if(err) {
+               SSLFreeBuffer(&modulus, ctx);
+               SSLFreeBuffer(&exponent, ctx);
+               return err;
+       }
+
+    if ((err = SSLAllocBuffer(keyParams,
+                       modulus.length + exponent.length + 4, ctx)) != 0) {
+        return err;
+       }
+    charPtr = keyParams->data;
+    charPtr = SSLEncodeInt(charPtr, modulus.length, 2);
+    memcpy(charPtr, modulus.data, modulus.length);
+    charPtr += modulus.length;
+    charPtr = SSLEncodeInt(charPtr, exponent.length, 2);
+    memcpy(charPtr, exponent.data, exponent.length);
+
+       /* these were mallocd by sslGetPubKeyBits() */
+       SSLFreeBuffer(&modulus, ctx);
+       SSLFreeBuffer(&exponent, ctx);
+    return noErr;
+#else
+    CFDataRef modulus = SecKeyCopyModulus(SECKEYREF(key));
+    if (!modulus) {
+               sslErrorLog("SSLEncodeRSAKeyParams: SecKeyCopyModulus failed\n");
+        return errSSLCrypto;
+       }
+    CFDataRef exponent = SecKeyCopyExponent(SECKEYREF(key));
+    if (!exponent) {
+               sslErrorLog("SSLEncodeRSAKeyParams: SecKeyCopyExponent failed\n");
+        CFRelease(modulus);
+        return errSSLCrypto;
+    }
+
+    CFIndex modulusLength = CFDataGetLength(modulus);
+    CFIndex exponentLength = CFDataGetLength(exponent);
+       sslDebugLog("SSLEncodeRSAKeyParams: modulus len=%ld, exponent len=%ld\n",
+               modulusLength, exponentLength);
+    OSStatus err;
+    if ((err = SSLAllocBuffer(keyParams,
+                       modulusLength + exponentLength + 4, ctx)) != 0) {
+        return err;
+       }
+    uint8_t *charPtr = keyParams->data;
+    charPtr = SSLEncodeSize(charPtr, modulusLength, 2);
+    memcpy(charPtr, CFDataGetBytePtr(modulus), modulusLength);
+    charPtr += modulusLength;
+    charPtr = SSLEncodeSize(charPtr, exponentLength, 2);
+    memcpy(charPtr, CFDataGetBytePtr(exponent), exponentLength);
+    CFRelease(modulus);
+    CFRelease(exponent);
+    return noErr;
+#endif
+}
+
+static OSStatus
+SSLEncodeRSAPremasterSecret(SSLContext *ctx)
+{   SSLBuffer           randData;
+    OSStatus            err;
+    SSLProtocolVersion maxVersion;
+
+    if ((err = SSLAllocBuffer(&ctx->preMasterSecret,
+                       SSL_RSA_PREMASTER_SECRET_SIZE, ctx)) != 0)
+        return err;
+
+       assert(ctx->negProtocolVersion >= SSL_Version_3_0);
+       sslGetMaxProtVersion(ctx, &maxVersion);
+    SSLEncodeInt(ctx->preMasterSecret.data, maxVersion, 2);
+    randData.data = ctx->preMasterSecret.data+2;
+    randData.length = SSL_RSA_PREMASTER_SECRET_SIZE - 2;
+    if ((err = sslRand(ctx, &randData)) != 0)
+        return err;
+    return noErr;
+}
+
+/*
+ * Generate a server key exchange message signed by our RSA or DSA private key.
+ */
+
+static OSStatus
+SSLSignServerKeyExchangeTls12(SSLContext *ctx, SSLSignatureAndHashAlgorithm sigAlg, SSLBuffer exchangeParams, SSLBuffer signature, size_t *actSigLen)
+{
+    OSStatus        err;
+    SSLBuffer       hashOut, hashCtx, clientRandom, serverRandom;
+    uint8_t         hashes[SSL_MAX_DIGEST_LEN];
+    SSLBuffer       signedHashes;
+    uint8_t                    *dataToSign;
+       size_t                  dataToSignLen;
+    const HashReference *hashRef;
+    SecAsn1AlgId        algId;
+
+       signedHashes.data = 0;
+    hashCtx.data = 0;
+
+    clientRandom.data = ctx->clientRandom;
+    clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+    serverRandom.data = ctx->serverRandom;
+    serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+
+    switch (sigAlg.hash) {
+        case SSL_HashAlgorithmSHA1:
+            hashRef = &SSLHashSHA1;
+            algId.algorithm = CSSMOID_SHA1WithRSA;
+            break;
+        case SSL_HashAlgorithmSHA256:
+            hashRef = &SSLHashSHA256;
+            algId.algorithm = CSSMOID_SHA256WithRSA;
+            break;
+        case SSL_HashAlgorithmSHA384:
+            hashRef = &SSLHashSHA384;
+            algId.algorithm = CSSMOID_SHA384WithRSA;
+            break;
+        default:
+            sslErrorLog("SSLVerifySignedServerKeyExchangeTls12: unsupported hash %d\n", sigAlg.hash);
+            return errSSLProtocol;
+    }
+
+
+    dataToSign = hashes;
+    dataToSignLen = hashRef->digestSize;
+    hashOut.data = hashes;
+    hashOut.length = hashRef->digestSize;
+
+    if ((err = ReadyHash(hashRef, &hashCtx, ctx)) != 0)
+        goto fail;
+    if ((err = hashRef->update(&hashCtx, &clientRandom)) != 0)
+        goto fail;
+    if ((err = hashRef->update(&hashCtx, &serverRandom)) != 0)
+        goto fail;
+    if ((err = hashRef->update(&hashCtx, &exchangeParams)) != 0)
+        goto fail;
+    if ((err = hashRef->final(&hashCtx, &hashOut)) != 0)
+        goto fail;
+
+    if(sigAlg.signature==SSL_SignatureAlgorithmRSA) {
+        err = sslRsaSign(ctx,
+                         ctx->signingPrivKeyRef,
+                         &algId,
+                         dataToSign,
+                         dataToSignLen,
+                         signature.data,
+                         signature.length,
+                         actSigLen);
+    } else {
+        err = sslRawSign(ctx,
+                         ctx->signingPrivKeyRef,
+                         dataToSign,                   // one or two hashes
+                         dataToSignLen,
+                         signature.data,
+                         signature.length,
+                         actSigLen);
+       }
+
+    if(err) {
+               sslErrorLog("SSLDecodeSignedServerKeyExchangeTls12: sslRawVerify "
+                    "returned %d\n", (int)err);
+               goto fail;
+       }
+
+fail:
+    SSLFreeBuffer(&signedHashes, ctx);
+    SSLFreeBuffer(&hashCtx, ctx);
+    return err;
+}
+
+static OSStatus
+SSLSignServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer exchangeParams, SSLBuffer signature, size_t *actSigLen)
+{
+    OSStatus        err;
+    uint8_t         hashes[SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN];
+    SSLBuffer       clientRandom,serverRandom,hashCtx, hash;
+       uint8_t                 *dataToSign;
+       size_t                  dataToSignLen;
+
+    hashCtx.data = 0;
+
+    /* cook up hash(es) for raw sign */
+    clientRandom.data   = ctx->clientRandom;
+    clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+    serverRandom.data   = ctx->serverRandom;
+    serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+
+    if(isRsa) {
+        /* skip this if signing with DSA */
+        dataToSign = hashes;
+        dataToSignLen = SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN;
+        hash.data = &hashes[0];
+        hash.length = SSL_MD5_DIGEST_LEN;
+
+        if ((err = ReadyHash(&SSLHashMD5, &hashCtx, ctx)) != 0)
+            goto fail;
+        if ((err = SSLHashMD5.update(&hashCtx, &clientRandom)) != 0)
+            goto fail;
+        if ((err = SSLHashMD5.update(&hashCtx, &serverRandom)) != 0)
+            goto fail;
+        if ((err = SSLHashMD5.update(&hashCtx, &exchangeParams)) != 0)
+            goto fail;
+        if ((err = SSLHashMD5.final(&hashCtx, &hash)) != 0)
+            goto fail;
+        if ((err = SSLFreeBuffer(&hashCtx, ctx)) != 0)
+            goto fail;
+    }
+    else {
+        /* DSA - just use the SHA1 hash */
+        dataToSign = &hashes[SSL_MD5_DIGEST_LEN];
+        dataToSignLen = SSL_SHA1_DIGEST_LEN;
+    }
+    hash.data = &hashes[SSL_MD5_DIGEST_LEN];
+    hash.length = SSL_SHA1_DIGEST_LEN;
+    if ((err = ReadyHash(&SSLHashSHA1, &hashCtx, ctx)) != 0)
+    goto fail;
+    if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0)
+    goto fail;
+    if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
+    goto fail;
+    if ((err = SSLHashSHA1.update(&hashCtx, &exchangeParams)) != 0)
+    goto fail;
+    if ((err = SSLHashSHA1.final(&hashCtx, &hash)) != 0)
+    goto fail;
+    if ((err = SSLFreeBuffer(&hashCtx, ctx)) != 0)
+    goto fail;
+
+
+    err = sslRawSign(ctx,
+                     ctx->signingPrivKeyRef,
+                     dataToSign,                       // one or two hashes
+                     dataToSignLen,
+                     signature.data,
+                     signature.length,
+                     actSigLen);
+    if(err) {
+        goto fail;
+    }
+
+fail:
+    SSLFreeBuffer(&hashCtx, ctx);
+
+    return err;
+}
+
+static
+OSStatus FindSigAlg(SSLContext *ctx,
+                    SSLSignatureAndHashAlgorithm *alg)
+{
+    unsigned i;
+
+    assert(ctx->protocolSide == kSSLServerSide);
+    assert(ctx->negProtocolVersion >= TLS_Version_1_2);
+    assert(!ctx->isDTLS);
+
+    if((ctx->numClientSigAlgs==0) ||(ctx->clientSigAlgs==NULL))
+        return errSSLInternal;
+
+    //FIXME: Need a better way to select here
+    for(i=0; i<ctx->numClientSigAlgs; i++) {
+        alg->hash = ctx->clientSigAlgs[i].hash;
+        alg->signature = ctx->clientSigAlgs[i].signature;
+        //We only support RSA for certs on the server side - but we should test against the cert type
+        if(ctx->clientSigAlgs[i].signature != SSL_SignatureAlgorithmRSA)
+            continue;
+        //Let's only support SHA1 and SHA256. SHA384 does not work with 512 bits keys.
+        // We should actually test against what the cert can do.
+        if((alg->hash==SSL_HashAlgorithmSHA1) || (alg->hash==SSL_HashAlgorithmSHA256)) {
+            return noErr;
+        }
+    }
+    // We could not find a supported signature and hash algorithm
+    return errSSLProtocol;
+}
+
+static OSStatus
+SSLEncodeSignedServerKeyExchange(SSLRecord *keyExch, SSLContext *ctx)
+{   OSStatus        err;
+    uint8_t         *charPtr;
+    size_t          outputLen;
+       bool                    isRsa = true;
+    size_t                     maxSigLen;
+    size_t             actSigLen;
+       SSLBuffer               signature;
+    int             head = 4;
+    SSLBuffer       exchangeParams;
+
+    assert(ctx->protocolSide == kSSLServerSide);
+       assert(ctx->signingPubKey != NULL);
+       assert(ctx->negProtocolVersion >= SSL_Version_3_0);
+    exchangeParams.data = 0;
+       signature.data = 0;
+
+#if ENABLE_DTLS
+    if(ctx->negProtocolVersion == DTLS_Version_1_0) {
+        head+=8;
+    }
+#endif
+
+
+       /* Set up parameter block to hash ==> exchangeParams */
+       switch(ctx->selectedCipherSpec.keyExchangeMethod) {
+               case SSL_RSA:
+        case SSL_RSA_EXPORT:
+                       /*
+                        * Parameter block = encryption public key.
+                        * If app hasn't supplied a separate encryption cert, abort.
+                        */
+                       if(ctx->encryptPubKey == NULL) {
+                               sslErrorLog("RSAServerKeyExchange: no encrypt cert\n");
+                               return errSSLBadConfiguration;
+                       }
+                       err = SSLEncodeRSAKeyParams(&exchangeParams,
+                               ctx->encryptPubKey, ctx);
+                       break;
+
+#if APPLE_DH
+
+               case SSL_DHE_DSS:
+               case SSL_DHE_DSS_EXPORT:
+                       isRsa = false;
+                       /* and fall through */
+               case SSL_DHE_RSA:
+               case SSL_DHE_RSA_EXPORT:
+               {
+                       /*
+                        * Parameter block = {prime, generator, public key}
+                        * Obtain D-H parameters (if we don't have them) and a key pair.
+                        */
+                       err = SSLGenServerDHParamsAndKey(ctx);
+                       if(err) {
+                               return err;
+                       }
+            size_t len = SSLEncodedDHKeyParamsLen(ctx);
+                       err = SSLAllocBuffer(&exchangeParams, len, ctx);
+                       if(err) {
+                               goto fail;
+                       }
+                       err = SSLEncodeDHKeyParams(ctx, exchangeParams.data);
+                       break;
+               }
+
+#endif /* APPLE_DH */
+
+               default:
+                       /* shouldn't be here */
+                       assert(0);
+                       return errSSLInternal;
+       }
+
+    SSLSignatureAndHashAlgorithm sigAlg;
+
+
+    /* preallocate a buffer for signing */
+    err = sslGetMaxSigSize(ctx->signingPrivKeyRef, &maxSigLen);
+    if(err) {
+        goto fail;
+    }
+    err = SSLAllocBuffer(&signature, maxSigLen, ctx);
+    if(err) {
+        goto fail;
+    }
+
+    outputLen = exchangeParams.length + 2;
+
+    if (sslVersionIsLikeTls12(ctx))
+    {
+        err=FindSigAlg(ctx, &sigAlg);
+        if(err)
+            goto fail;
+
+        outputLen += 2;
+        err = SSLSignServerKeyExchangeTls12(ctx, sigAlg, exchangeParams,
+                                                    signature, &actSigLen);
+    } else {
+        err = SSLSignServerKeyExchange(ctx, isRsa, exchangeParams,
+                                               signature, &actSigLen);
+    }
+
+    if(err)
+        goto fail;
+
+    assert(actSigLen <= maxSigLen);
+
+    outputLen += actSigLen;
+
+       /* package it all up */
+    keyExch->protocolVersion = ctx->negProtocolVersion;
+    keyExch->contentType = SSL_RecordTypeHandshake;
+    if ((err = SSLAllocBuffer(&keyExch->contents, outputLen+head, ctx)) != 0)
+        goto fail;
+
+    charPtr = SSLEncodeHandshakeHeader(ctx, keyExch, SSL_HdskServerKeyExchange, outputLen);
+
+    memcpy(charPtr, exchangeParams.data, exchangeParams.length);
+    charPtr += exchangeParams.length;
+
+    if (sslVersionIsLikeTls12(ctx))
+    {
+        *charPtr++=sigAlg.hash;
+        *charPtr++=sigAlg.signature;
+    }
+
+    charPtr = SSLEncodeInt(charPtr, actSigLen, 2);
+       memcpy(charPtr, signature.data, actSigLen);
+    assert((charPtr + actSigLen) ==
+                  (keyExch->contents.data + keyExch->contents.length));
+
+    err = noErr;
+
+fail:
+    SSLFreeBuffer(&exchangeParams, ctx);
+    SSLFreeBuffer(&signature, ctx);
+    return err;
+}
+
+static OSStatus
+SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,
+                                 uint8_t *signature, UInt16 signatureLen)
+{
+    OSStatus        err;
+    SSLBuffer       hashOut, hashCtx, clientRandom, serverRandom;
+    uint8_t         hashes[SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN];
+    SSLBuffer       signedHashes;
+    uint8_t                    *dataToSign;
+       size_t                  dataToSignLen;
+
+       signedHashes.data = 0;
+    hashCtx.data = 0;
+
+    clientRandom.data = ctx->clientRandom;
+    clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+    serverRandom.data = ctx->serverRandom;
+    serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+
+
+       if(isRsa) {
+               /* skip this if signing with DSA */
+               dataToSign = hashes;
+               dataToSignLen = SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN;
+               hashOut.data = hashes;
+               hashOut.length = SSL_MD5_DIGEST_LEN;
+
+               if ((err = ReadyHash(&SSLHashMD5, &hashCtx, ctx)) != 0)
+                       goto fail;
+               if ((err = SSLHashMD5.update(&hashCtx, &clientRandom)) != 0)
+                       goto fail;
+               if ((err = SSLHashMD5.update(&hashCtx, &serverRandom)) != 0)
+                       goto fail;
+               if ((err = SSLHashMD5.update(&hashCtx, &signedParams)) != 0)
+                       goto fail;
+               if ((err = SSLHashMD5.final(&hashCtx, &hashOut)) != 0)
+                       goto fail;
+       }
+       else {
+               /* DSA, ECDSA - just use the SHA1 hash */
+               dataToSign = &hashes[SSL_MD5_DIGEST_LEN];
+               dataToSignLen = SSL_SHA1_DIGEST_LEN;
+       }
+
+       hashOut.data = hashes + SSL_MD5_DIGEST_LEN;
+    hashOut.length = SSL_SHA1_DIGEST_LEN;
+    if ((err = SSLFreeBuffer(&hashCtx, ctx)) != 0)
+        goto fail;
+
+    if ((err = ReadyHash(&SSLHashSHA1, &hashCtx, ctx)) != 0)
+        goto fail;
+    if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0)
+        goto fail;
+    if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
+        goto fail;
+    if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
+        goto fail;
+    if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
+        goto fail;
+
+       err = sslRawVerify(ctx,
+                       ctx->peerPubKey,
+                       dataToSign,                             /* plaintext */
+                       dataToSignLen,                  /* plaintext length */
+                       signature,
+                       signatureLen);
+       if(err) {
+               sslErrorLog("SSLDecodeSignedServerKeyExchange: sslRawVerify "
+                    "returned %d\n", (int)err);
+               goto fail;
+       }
+
+fail:
+    SSLFreeBuffer(&signedHashes, ctx);
+    SSLFreeBuffer(&hashCtx, ctx);
+    return err;
+
+}
+
+static OSStatus
+SSLVerifySignedServerKeyExchangeTls12(SSLContext *ctx, SSLSignatureAndHashAlgorithm sigAlg, SSLBuffer signedParams,
+                                 uint8_t *signature, UInt16 signatureLen)
+{
+    OSStatus        err;
+    SSLBuffer       hashOut, hashCtx, clientRandom, serverRandom;
+    uint8_t         hashes[SSL_MAX_DIGEST_LEN];
+    SSLBuffer       signedHashes;
+    uint8_t                    *dataToSign;
+       size_t                  dataToSignLen;
+    const HashReference *hashRef;
+    SecAsn1AlgId        algId;
+
+       signedHashes.data = 0;
+    hashCtx.data = 0;
+
+    clientRandom.data = ctx->clientRandom;
+    clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+    serverRandom.data = ctx->serverRandom;
+    serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+
+    switch (sigAlg.hash) {
+        case SSL_HashAlgorithmSHA1:
+            hashRef = &SSLHashSHA1;
+            algId.algorithm = CSSMOID_SHA1WithRSA;
+            break;
+        case SSL_HashAlgorithmSHA256:
+            hashRef = &SSLHashSHA256;
+            algId.algorithm = CSSMOID_SHA256WithRSA;
+            break;
+        case SSL_HashAlgorithmSHA384:
+            hashRef = &SSLHashSHA384;
+            algId.algorithm = CSSMOID_SHA384WithRSA;
+            break;
+        default:
+            sslErrorLog("SSLVerifySignedServerKeyExchangeTls12: unsupported hash %d\n", sigAlg.hash);
+            return errSSLProtocol;
+    }
+
+
+    dataToSign = hashes;
+    dataToSignLen = hashRef->digestSize;
+    hashOut.data = hashes;
+    hashOut.length = hashRef->digestSize;
+
+    if ((err = ReadyHash(hashRef, &hashCtx, ctx)) != 0)
+        goto fail;
+    if ((err = hashRef->update(&hashCtx, &clientRandom)) != 0)
+        goto fail;
+    if ((err = hashRef->update(&hashCtx, &serverRandom)) != 0)
+        goto fail;
+    if ((err = hashRef->update(&hashCtx, &signedParams)) != 0)
+        goto fail;
+    if ((err = hashRef->final(&hashCtx, &hashOut)) != 0)
+        goto fail;
+
+    if(sigAlg.signature==SSL_SignatureAlgorithmRSA) {
+        err = sslRsaVerify(ctx,
+                           ctx->peerPubKey,
+                           &algId,
+                           dataToSign,
+                           dataToSignLen,
+                           signature,
+                           signatureLen);
+    } else {
+        err = sslRawVerify(ctx,
+                           ctx->peerPubKey,
+                           dataToSign,                         /* plaintext */
+                           dataToSignLen,                      /* plaintext length */
+                           signature,
+                           signatureLen);
+       }
+
+    if(err) {
+               sslErrorLog("SSLDecodeSignedServerKeyExchangeTls12: sslRawVerify "
+                    "returned %d\n", (int)err);
+               goto fail;
+       }
+
+fail:
+    SSLFreeBuffer(&signedHashes, ctx);
+    SSLFreeBuffer(&hashCtx, ctx);
+    return err;
+
+}
+
+/*
+ * Decode and verify a server key exchange message signed by server's
+ * public key.
+ */
+static OSStatus
+SSLDecodeSignedServerKeyExchange(SSLBuffer message, SSLContext *ctx)
+{
+       OSStatus        err;
+    UInt16          modulusLen = 0, exponentLen = 0, signatureLen;
+    uint8_t         *modulus = NULL, *exponent = NULL, *signature;
+       bool                    isRsa = true;
+
+       assert(ctx->protocolSide == kSSLClientSide);
+
+    if (message.length < 2) {
+       sslErrorLog("SSLDecodeSignedServerKeyExchange: msg len error 1\n");
+        return errSSLProtocol;
+    }
+
+       /* first extract the key-exchange-method-specific parameters */
+    uint8_t *charPtr = message.data;
+       uint8_t *endCp = charPtr + message.length;
+       switch(ctx->selectedCipherSpec.keyExchangeMethod) {
+               case SSL_RSA:
+        case SSL_RSA_EXPORT:
+                       modulusLen = SSLDecodeInt(charPtr, 2);
+                       charPtr += 2;
+                       if((charPtr + modulusLen) > endCp) {
+                               sslErrorLog("signedServerKeyExchange: msg len error 2\n");
+                               return errSSLProtocol;
+                       }
+                       modulus = charPtr;
+                       charPtr += modulusLen;
+
+                       exponentLen = SSLDecodeInt(charPtr, 2);
+                       charPtr += 2;
+                       if((charPtr + exponentLen) > endCp) {
+                               sslErrorLog("signedServerKeyExchange: msg len error 3\n");
+                               return errSSLProtocol;
+                       }
+                       exponent = charPtr;
+                       charPtr += exponentLen;
+                       break;
+#if APPLE_DH
+               case SSL_DHE_DSS:
+               case SSL_DHE_DSS_EXPORT:
+                       isRsa = false;
+                       /* and fall through */
+               case SSL_DHE_RSA:
+               case SSL_DHE_RSA_EXPORT:
+                       err = SSLDecodeDHKeyParams(ctx, &charPtr, message.length);
+                       if(err) {
+                               return err;
+                       }
+                       break;
+               #endif  /* APPLE_DH */
+
+               case SSL_ECDHE_ECDSA:
+                       isRsa = false;
+                       /* and fall through */
+               case SSL_ECDHE_RSA:
+                       err = SSLDecodeECDHKeyParams(ctx, &charPtr, message.length);
+                       if(err) {
+                               return err;
+                       }
+                       break;
+               default:
+                       assert(0);
+                       return errSSLInternal;
+       }
+
+       /* this is what's hashed */
+       SSLBuffer signedParams;
+       signedParams.data = message.data;
+       signedParams.length = charPtr - message.data;
+
+    SSLSignatureAndHashAlgorithm sigAlg;
+
+    if (sslVersionIsLikeTls12(ctx)) {
+        /* Parse the algorithm field added in TLS1.2 */
+        if((charPtr + 2) > endCp) {
+            sslErrorLog("signedServerKeyExchange: msg len error 499\n");
+            return errSSLProtocol;
+        }
+        sigAlg.hash = *charPtr++;
+        sigAlg.signature = *charPtr++;
+    }
+
+       signatureLen = SSLDecodeInt(charPtr, 2);
+       charPtr += 2;
+       if((charPtr + signatureLen) != endCp) {
+               sslErrorLog("signedServerKeyExchange: msg len error 4\n");
+               return errSSLProtocol;
+       }
+       signature = charPtr;
+
+    if (sslVersionIsLikeTls12(ctx))
+    {
+        err = SSLVerifySignedServerKeyExchangeTls12(ctx, sigAlg, signedParams,
+                                                    signature, signatureLen);
+    } else {
+        err = SSLVerifySignedServerKeyExchange(ctx, isRsa, signedParams,
+                                               signature, signatureLen);
+    }
+
+    if(err)
+        goto fail;
+
+       /* Signature matches; now replace server key with new key (RSA only) */
+       switch(ctx->selectedCipherSpec.keyExchangeMethod) {
+               case SSL_RSA:
+        case SSL_RSA_EXPORT:
+               {
+                       SSLBuffer modBuf;
+                       SSLBuffer expBuf;
+
+                       /* first free existing peerKey */
+                       sslFreePubKey(&ctx->peerPubKey);                                        /* no KCItem */
+
+                       /* and cook up a new one from raw bits */
+                       modBuf.data = modulus;
+                       modBuf.length = modulusLen;
+                       expBuf.data = exponent;
+                       expBuf.length = exponentLen;
+                       err = sslGetPubKeyFromBits(ctx,
+                               &modBuf,
+                               &expBuf,
+                               &ctx->peerPubKey);
+                       break;
+               }
+               case SSL_DHE_RSA:
+               case SSL_DHE_RSA_EXPORT:
+               case SSL_DHE_DSS:
+               case SSL_DHE_DSS_EXPORT:
+               case SSL_ECDHE_ECDSA:
+               case SSL_ECDHE_RSA:
+                       break;                                  /* handled above */
+               default:
+                       assert(0);
+       }
+fail:
+    return err;
+}
+
+static OSStatus
+SSLDecodeRSAKeyExchange(SSLBuffer keyExchange, SSLContext *ctx)
+{   OSStatus            err;
+    size_t                     outputLen, localKeyModulusLen;
+    SSLProtocolVersion  version;
+    Boolean                            useEncryptKey = false;
+       uint8_t                         *src = NULL;
+       SSLPrivKey                      *keyRef = NULL;
+
+       assert(ctx->protocolSide == kSSLServerSide);
+
+       #if             SSL_SERVER_KEYEXCH_HACK
+               /*
+                * the way we work with Netscape.
+                * FIXME - maybe we should *require* an encryptPrivKey in this
+                * situation?
+                */
+               if((ctx->selectedCipherSpec.keyExchangeMethod == SSL_RSA_EXPORT) &&
+                       (ctx->encryptPrivKey != NULL)) {
+                       useEncryptKey = true;
+               }
+
+       #else   /* !SSL_SERVER_KEYEXCH_HACK */
+               /* The "correct" way, I think, which doesn't work with Netscape */
+               if (ctx->encryptPrivKeyRef) {
+                       useEncryptKey = true;
+               }
+       #endif  /* SSL_SERVER_KEYEXCH_HACK */
+       if (useEncryptKey) {
+               keyRef  = ctx->encryptPrivKeyRef;
+               /* FIXME: when 3420180 is implemented, pick appropriate creds here */
+       }
+       else {
+               keyRef  = ctx->signingPrivKeyRef;
+               /* FIXME: when 3420180 is implemented, pick appropriate creds here */
+       }
+
+       localKeyModulusLen = sslPrivKeyLengthInBytes(keyRef);
+       if (localKeyModulusLen == 0) {
+               sslErrorLog("SSLDecodeRSAKeyExchange: private key modulus is 0\n");
+               return errSSLCrypto;
+       }
+
+       /*
+        * We have to tolerate incoming key exchange msgs with and without the
+        * two-byte "encrypted length" field.
+        */
+    if (keyExchange.length == localKeyModulusLen) {
+               /* no length encoded */
+               src = keyExchange.data;
+       }
+       else if((keyExchange.length == (localKeyModulusLen + 2)) &&
+               (ctx->negProtocolVersion >= TLS_Version_1_0)) {
+               /* TLS only - skip the length bytes */
+               src = keyExchange.data + 2;
+       }
+       else {
+       sslErrorLog("SSLDecodeRSAKeyExchange: length error (exp %u got %u)\n",
+                       (unsigned)localKeyModulusLen, (unsigned)keyExchange.length);
+        return errSSLProtocol;
+       }
+    err = SSLAllocBuffer(&ctx->preMasterSecret, SSL_RSA_PREMASTER_SECRET_SIZE, ctx);
+       if(err != 0) {
+        return err;
+       }
+
+       /*
+        * From this point on, to defend against the Bleichenbacher attack
+        * and its Klima-Pokorny-Rosa variant, any errors we detect are *not*
+        * reported to the caller or the peer. If we detect any error during
+        * decryption (e.g., bad PKCS1 padding) or in the testing of the version
+        * number in the premaster secret, we proceed by generating a random
+        * premaster secret, with the correct version number, and tell our caller
+        * that everything is fine. This session will fail as soon as the
+        * finished messages are sent, since we will be using a bogus premaster
+        * secret (and hence bogus session and MAC keys). Meanwhile we have
+        * not provided any side channel information relating to the cause of
+        * the failure.
+        *
+        * See http://eprint.iacr.org/2003/052/ for more info.
+        */
+       err = sslRsaDecrypt(ctx,
+               keyRef,
+#if USE_CDSA_CRYPTO
+               CSSM_PADDING_PKCS1,
+#else
+               kSecPaddingPKCS1,
+#endif
+               src,
+               localKeyModulusLen,                             // ciphertext len
+               ctx->preMasterSecret.data,
+               SSL_RSA_PREMASTER_SECRET_SIZE,  // plaintext buf available
+               &outputLen);
+
+       if(err != noErr) {
+               /* possible Bleichenbacher attack */
+               sslLogNegotiateDebug("SSLDecodeRSAKeyExchange: RSA decrypt fail");
+       }
+       else if(outputLen != SSL_RSA_PREMASTER_SECRET_SIZE) {
+               sslLogNegotiateDebug("SSLDecodeRSAKeyExchange: premaster secret size error");
+       err = errSSLProtocol;                                                   // not passed back to caller
+    }
+
+       if(err == noErr) {
+               /*
+                * Two legal values here - the one we actually negotiated (which is
+                * technically incorrect but not uncommon), and the one the client
+                * sent as its preferred version in the client hello msg.
+                */
+               version = (SSLProtocolVersion)SSLDecodeInt(ctx->preMasterSecret.data, 2);
+               if((version != ctx->negProtocolVersion) &&
+                  (version != ctx->clientReqProtocol)) {
+                       /* possible Klima-Pokorny-Rosa attack */
+                       sslLogNegotiateDebug("SSLDecodeRSAKeyExchange: version error");
+                       err = errSSLProtocol;
+               }
+    }
+       if(err != noErr) {
+               /*
+                * Obfuscate failures for defense against Bleichenbacher and
+                * Klima-Pokorny-Rosa attacks.
+                */
+               SSLEncodeInt(ctx->preMasterSecret.data, ctx->negProtocolVersion, 2);
+               SSLBuffer tmpBuf;
+               tmpBuf.data   = ctx->preMasterSecret.data + 2;
+               tmpBuf.length = SSL_RSA_PREMASTER_SECRET_SIZE - 2;
+               /* must ignore failures here */
+               sslRand(ctx, &tmpBuf);
+       }
+
+       /* in any case, save premaster secret (good or bogus) and proceed */
+    return noErr;
+}
+
+static OSStatus
+SSLEncodeRSAKeyExchange(SSLRecord *keyExchange, SSLContext *ctx)
+{   OSStatus            err;
+    size_t                     outputLen, peerKeyModulusLen;
+    size_t                             bufLen;
+       uint8_t                         *dst;
+       bool                            encodeLen = false;
+    uint8_t             *p;
+    int                 head;
+    size_t              msglen;
+
+       assert(ctx->protocolSide == kSSLClientSide);
+    if ((err = SSLEncodeRSAPremasterSecret(ctx)) != 0)
+        return err;
+
+    keyExchange->contentType = SSL_RecordTypeHandshake;
+       assert(ctx->negProtocolVersion >= SSL_Version_3_0);
+    keyExchange->protocolVersion = ctx->negProtocolVersion;
+
+    peerKeyModulusLen = sslPubKeyLengthInBytes(ctx->peerPubKey);
+       if (peerKeyModulusLen == 0) {
+               sslErrorLog("SSLEncodeRSAKeyExchange: peer key modulus is 0\n");
+               /* FIXME: we don't return an error here... is this condition ever expected? */
+       }
+#if SSL_DEBUG
+       sslDebugLog("SSLEncodeRSAKeyExchange: peer key modulus length = %lu\n", peerKeyModulusLen);
+#endif
+    msglen = peerKeyModulusLen;
+       #if     RSA_CLIENT_KEY_ADD_LENGTH
+       if(ctx->negProtocolVersion >= TLS_Version_1_0) {
+        msglen += 2;
+               encodeLen = true;
+       }
+       #endif
+    head = SSLHandshakeHeaderSize(keyExchange);
+    bufLen = msglen + head;
+    if ((err = SSLAllocBuffer(&keyExchange->contents,
+               bufLen,ctx)) != 0)
+    {
+        return err;
+    }
+       dst = keyExchange->contents.data + head;
+       if(encodeLen) {
+               dst += 2;
+       }
+
+       /* FIXME: can this line be removed? */
+    p = keyExchange->contents.data;
+
+    p = SSLEncodeHandshakeHeader(ctx, keyExchange, SSL_HdskClientKeyExchange, msglen);
+
+       if(encodeLen) {
+               /* the length of the encrypted pre_master_secret */
+               SSLEncodeSize(keyExchange->contents.data + head,
+                       peerKeyModulusLen, 2);
+       }
+       err = sslRsaEncrypt(ctx,
+               ctx->peerPubKey,
+#if USE_CDSA_CRYPTO
+               CSSM_PADDING_PKCS1,
+#else
+               kSecPaddingPKCS1,
+#endif
+               ctx->preMasterSecret.data,
+               SSL_RSA_PREMASTER_SECRET_SIZE,
+               dst,
+               peerKeyModulusLen,
+               &outputLen);
+       if(err) {
+               sslErrorLog("SSLEncodeRSAKeyExchange: error %d\n", err);
+               return err;
+       }
+
+    assert(outputLen == (encodeLen ? msglen - 2 : msglen));
+
+    return noErr;
+}
+
+
+#if APPLE_DH
+
+#pragma mark -
+#pragma mark Diffie-Hellman Key Exchange
+
+/*
+ * Diffie-Hellman setup, server side. On successful return, the
+ * following SSLContext members are valid:
+ *
+ *             dhParamsPrime
+ *             dhParamsGenerator
+ *             dhPrivate
+ *             dhExchangePublic
+ */
+static OSStatus
+SSLGenServerDHParamsAndKey(
+       SSLContext *ctx)
+{
+       OSStatus ortn;
+    assert(ctx->protocolSide == kSSLServerSide);
+
+
+    /*
+     * Obtain D-H parameters if we don't have them.
+     */
+    if(ctx->dhParamsEncoded.data == NULL) {
+        /* TODO: Pick appropriate group based on cipher suite */
+        ccdh_const_gp_t gp = ccdh_gp_rfc5114_MODP_2048_256();
+        cc_size n = ccdh_gp_n(gp);
+        size_t s = ccdh_gp_prime_size(gp);
+        uint8_t p[s];
+        uint8_t g[s];
+
+        ccn_write_uint(n, ccdh_gp_prime(gp), s, p);
+        ccn_write_uint(n, ccdh_gp_g(gp), s, g);
+
+        const SSLBuffer        prime = {
+            .data = p,
+            .length = s,
+        };
+        const SSLBuffer        generator = {
+            .data = g,
+            .length = s,
+        };
+
+        ortn=sslEncodeDhParams(&ctx->dhParamsEncoded,                  /* data mallocd and RETURNED PKCS-3 encoded */
+                               &prime,                 /* Wire format */
+                               &generator);     /* Wire format */
+
+        if(ortn)
+            return ortn;
+    }
+
+#if USE_CDSA_CRYPTO
+       /* generate per-session D-H key pair */
+       sslFreeKey(ctx->cspHand, &ctx->dhPrivate, NULL);
+       SSLFreeBuffer(&ctx->dhExchangePublic, ctx);
+       ctx->dhPrivate = (CSSM_KEY *)sslMalloc(sizeof(CSSM_KEY));
+       CSSM_KEY pubKey;
+       ortn = sslDhGenerateKeyPair(ctx,
+               &ctx->dhParamsEncoded,
+               ctx->dhParamsPrime.length * 8,
+               &pubKey, ctx->dhPrivate);
+       if(ortn) {
+               return ortn;
+       }
+       CSSM_TO_SSLBUF(&pubKey.KeyData, &ctx->dhExchangePublic);
+#else
+    if (!ctx->secDHContext) {
+        ortn = sslDhCreateKey(ctx);
+        if(ortn)
+            return ortn;
+    }
+    return sslDhGenerateKeyPair(ctx);
+#endif
+       return noErr;
+}
+
+/*
+ * size of DH param and public key, in wire format
+ */
+static size_t
+SSLEncodedDHKeyParamsLen(SSLContext *ctx)
+{
+    SSLBuffer prime;
+    SSLBuffer generator;
+
+    sslDecodeDhParams(&ctx->dhParamsEncoded, &prime, &generator);
+
+    return (2+prime.length+2+generator.length+2+ctx->dhExchangePublic.length);
+}
+
+/*
+ * Encode DH params and public key, in wire format, in caller-supplied buffer.
+ */
+static OSStatus
+SSLEncodeDHKeyParams(
+       SSLContext *ctx,
+       uint8_t *charPtr)
+{
+    assert(ctx->protocolSide == kSSLServerSide);
+       assert(ctx->dhParamsEncoded.data != NULL);
+       assert(ctx->dhExchangePublic.data != NULL);
+
+    SSLBuffer prime;
+    SSLBuffer generator;
+
+    sslDecodeDhParams(&ctx->dhParamsEncoded, &prime, &generator);
+
+       charPtr = SSLEncodeInt(charPtr, prime.length, 2);
+       memcpy(charPtr, prime.data, prime.length);
+       charPtr += prime.length;
+
+       charPtr = SSLEncodeInt(charPtr, generator.length, 2);
+       memcpy(charPtr, generator.data,
+               generator.length);
+       charPtr += generator.length;
+
+    /* TODO: hum.... sounds like this one should be in the SecDHContext */
+       charPtr = SSLEncodeInt(charPtr, ctx->dhExchangePublic.length, 2);
+       memcpy(charPtr, ctx->dhExchangePublic.data,
+               ctx->dhExchangePublic.length);
+
+       dumpBuf("server prime", &prime);
+       dumpBuf("server generator", &generator);
+       dumpBuf("server pub key", &ctx->dhExchangePublic);
+
+       return noErr;
+}
+
+/*
+ * Decode DH params and server public key.
+ */
+static OSStatus
+SSLDecodeDHKeyParams(
+       SSLContext *ctx,
+       uint8_t **charPtr,              // IN/OUT
+       size_t length)
+{
+       OSStatus        err = noErr;
+    SSLBuffer       prime;
+    SSLBuffer       generator;
+
+       assert(ctx->protocolSide == kSSLClientSide);
+    uint8_t *endCp = *charPtr + length;
+
+       /* Allow reuse via renegotiation */
+       SSLFreeBuffer(&ctx->dhPeerPublic, ctx);
+
+       /* Prime, with a two-byte length */
+       UInt32 len = SSLDecodeInt(*charPtr, 2);
+       (*charPtr) += 2;
+       if((*charPtr + len) > endCp) {
+               return errSSLProtocol;
+       }
+
+       prime.data = *charPtr;
+    prime.length = len;
+
+       (*charPtr) += len;
+
+       /* Generator, with a two-byte length */
+       len = SSLDecodeInt(*charPtr, 2);
+       (*charPtr) += 2;
+       if((*charPtr + len) > endCp) {
+               return errSSLProtocol;
+       }
+
+    generator.data = *charPtr;
+    generator.length = len;
+
+    (*charPtr) += len;
+
+    sslEncodeDhParams(&ctx->dhParamsEncoded, &prime, &generator);
+
+       /* peer public key, with a two-byte length */
+       len = SSLDecodeInt(*charPtr, 2);
+       (*charPtr) += 2;
+       err = SSLAllocBuffer(&ctx->dhPeerPublic, len, ctx);
+       if(err) {
+               return err;
+       }
+       memmove(ctx->dhPeerPublic.data, *charPtr, len);
+       (*charPtr) += len;
+
+       dumpBuf("client peer pub", &ctx->dhPeerPublic);
+       dumpBuf("client prime", &ctx->dhParamsPrime);
+       dumpBuf("client generator", &ctx->dhParamsGenerator);
+
+       return err;
+}
+
+/*
+ * Given the server's Diffie-Hellman parameters, generate our
+ * own DH key pair, and perform key exchange using the server's
+ * public key and our private key. The result is the premaster
+ * secret.
+ *
+ * SSLContext members valid on entry:
+ *             dhParamsPrime
+ *             dhParamsGenerator
+ *             dhPeerPublic
+ *
+ * SSLContext members valid on successful return:
+ *             dhPrivate
+ *             dhExchangePublic
+ *             preMasterSecret
+ */
+static OSStatus
+SSLGenClientDHKeyAndExchange(SSLContext *ctx)
+{
+       OSStatus            ortn;
+
+#if USE_CDSA_CRYPTO
+
+       if((ctx->dhParamsPrime.data == NULL) ||
+          (ctx->dhParamsGenerator.data == NULL) ||
+          (ctx->dhPeerPublic.data == NULL)) {
+          sslErrorLog("SSLGenClientDHKeyAndExchange: incomplete server params\n");
+          return errSSLProtocol;
+       }
+
+    /* generate two keys */
+       CSSM_KEY pubKey;
+       ctx->dhPrivate = (CSSM_KEY *)sslMalloc(sizeof(CSSM_KEY));
+       ortn = sslDhGenKeyPairClient(ctx,
+               &ctx->dhParamsPrime,
+               &ctx->dhParamsGenerator,
+               &pubKey, ctx->dhPrivate);
+       if(ortn) {
+               sslFree(ctx->dhPrivate);
+               ctx->dhPrivate = NULL;
+               return ortn;
+       }
+
+       /* do the exchange, size of prime */
+       ortn = sslDhKeyExchange(ctx, ctx->dhParamsPrime.length * 8,
+               &ctx->preMasterSecret);
+       if(ortn) {
+               return ortn;
+       }
+       CSSM_TO_SSLBUF(&pubKey.KeyData, &ctx->dhExchangePublic);
+#else
+    ortn=errSSLProtocol;
+    require(ctx->dhParamsEncoded.data, out);
+    require_noerr(ortn = sslDhCreateKey(ctx), out);
+    require_noerr(ortn = sslDhGenerateKeyPair(ctx), out);
+    require_noerr(ortn = sslDhKeyExchange(ctx), out);
+out:
+#endif
+       return ortn;
+}
+
+
+static OSStatus
+SSLEncodeDHanonServerKeyExchange(SSLRecord *keyExch, SSLContext *ctx)
+{
+       OSStatus            ortn = noErr;
+    int                 head;
+
+       assert(ctx->negProtocolVersion >= SSL_Version_3_0);
+       assert(ctx->protocolSide == kSSLServerSide);
+
+       /*
+        * Obtain D-H parameters (if we don't have them) and a key pair.
+        */
+       ortn = SSLGenServerDHParamsAndKey(ctx);
+       if(ortn) {
+               return ortn;
+       }
+
+       size_t length = SSLEncodedDHKeyParamsLen(ctx);
+
+       keyExch->protocolVersion = ctx->negProtocolVersion;
+       keyExch->contentType = SSL_RecordTypeHandshake;
+    head = SSLHandshakeHeaderSize(keyExch);
+       if ((ortn = SSLAllocBuffer(&keyExch->contents, length+head, ctx)) != 0)
+               return ortn;
+
+    uint8_t *charPtr = SSLEncodeHandshakeHeader(ctx, keyExch, SSL_HdskServerKeyExchange, length);
+
+       /* encode prime, generator, our public key */
+       return SSLEncodeDHKeyParams(ctx, charPtr);
+}
+
+static OSStatus
+SSLDecodeDHanonServerKeyExchange(SSLBuffer message, SSLContext *ctx)
+{
+       OSStatus        err = noErr;
+
+       assert(ctx->protocolSide == kSSLClientSide);
+    if (message.length < 6) {
+       sslErrorLog("SSLDecodeDHanonServerKeyExchange error: msg len %u\n",
+               (unsigned)message.length);
+        return errSSLProtocol;
+    }
+    uint8_t *charPtr = message.data;
+       err = SSLDecodeDHKeyParams(ctx, &charPtr, message.length);
+       if(err == noErr) {
+               if((message.data + message.length) != charPtr) {
+                       err = errSSLProtocol;
+               }
+       }
+       return err;
+}
+
+static OSStatus
+SSLDecodeDHClientKeyExchange(SSLBuffer keyExchange, SSLContext *ctx)
+{
+       OSStatus        ortn = noErr;
+    unsigned int    publicLen;
+
+       assert(ctx->protocolSide == kSSLServerSide);
+       if(ctx->dhParamsEncoded.data == NULL) {
+               /* should never happen */
+               assert(0);
+               return errSSLInternal;
+       }
+
+       /* this message simply contains the client's public DH key */
+       uint8_t *charPtr = keyExchange.data;
+    publicLen = SSLDecodeInt(charPtr, 2);
+       charPtr += 2;
+    /* TODO : Check the len here ? Will fail in sslDhKeyExchange anyway */
+    /*
+       if((keyExchange.length != publicLen + 2) ||
+          (publicLen > ctx->dhParamsPrime.length)) {
+        return errSSLProtocol;
+    }
+    */
+       SSLFreeBuffer(&ctx->dhPeerPublic, ctx); // allow reuse via renegotiation
+       ortn = SSLAllocBuffer(&ctx->dhPeerPublic, publicLen, ctx);
+       if(ortn) {
+               return ortn;
+       }
+       memmove(ctx->dhPeerPublic.data, charPtr, publicLen);
+
+       /* DH Key exchange, result --> premaster secret */
+       SSLFreeBuffer(&ctx->preMasterSecret, ctx);
+#if USE_CDSA_CRYPTO
+       ortn = sslDhKeyExchange(ctx, ctx->dhParamsPrime.length * 8,
+               &ctx->preMasterSecret);
+#else
+    ortn = sslDhKeyExchange(ctx);
+#endif
+       dumpBuf("server peer pub", &ctx->dhPeerPublic);
+       dumpBuf("server premaster", &ctx->preMasterSecret);
+       return ortn;
+}
+
+static OSStatus
+SSLEncodeDHClientKeyExchange(SSLRecord *keyExchange, SSLContext *ctx)
+{   OSStatus            err;
+    size_t                             outputLen;
+    int                 head;
+
+       assert(ctx->protocolSide == kSSLClientSide);
+       assert(ctx->negProtocolVersion >= SSL_Version_3_0);
+
+    keyExchange->contentType = SSL_RecordTypeHandshake;
+    keyExchange->protocolVersion = ctx->negProtocolVersion;
+
+    if ((err = SSLGenClientDHKeyAndExchange(ctx)) != 0)
+        return err;
+
+    outputLen = ctx->dhExchangePublic.length + 2;
+    head = SSLHandshakeHeaderSize(keyExchange);
+    if ((err = SSLAllocBuffer(&keyExchange->contents,outputLen + head,ctx)) != 0)
+        return err;
+
+    uint8_t *charPtr = SSLEncodeHandshakeHeader(ctx, keyExchange, SSL_HdskClientKeyExchange, outputLen);
+
+    charPtr = SSLEncodeSize(charPtr, ctx->dhExchangePublic.length, 2);
+    memcpy(charPtr, ctx->dhExchangePublic.data, ctx->dhExchangePublic.length);
+
+       dumpBuf("client pub key", &ctx->dhExchangePublic);
+       dumpBuf("client premaster", &ctx->preMasterSecret);
+
+    return noErr;
+}
+
+#endif /* APPLE_DH */
+
+#pragma mark -
+#pragma mark ECDSA Key Exchange
+
+/*
+ * Given the server's ECDH curve params and public key, generate our
+ * own ECDH key pair, and perform key exchange using the server's
+ * public key and our private key. The result is the premaster
+ * secret.
+ *
+ * SSLContext members valid on entry:
+ *      if keyExchangeMethod == SSL_ECDHE_ECDSA or SSL_ECDHE_RSA:
+ *                     ecdhPeerPublic
+ *                     ecdhPeerCurve
+ *             if keyExchangeMethod == SSL_ECDH_ECDSA or SSL_ECDH_RSA:
+ *                     peerPubKey, from which we infer ecdhPeerCurve
+ *
+ * SSLContext members valid on successful return:
+ *             ecdhPrivate
+ *             ecdhExchangePublic
+ *             preMasterSecret
+ */
+static OSStatus
+SSLGenClientECDHKeyAndExchange(SSLContext *ctx)
+{
+       OSStatus            ortn;
+
+    assert(ctx->protocolSide == kSSLClientSide);
+
+       switch(ctx->selectedCipherSpec.keyExchangeMethod) {
+               case SSL_ECDHE_ECDSA:
+               case SSL_ECDHE_RSA:
+                       /* Server sent us an ephemeral key with peer curve specified */
+                       if(ctx->ecdhPeerPublic.data == NULL) {
+                          sslErrorLog("SSLGenClientECDHKeyAndExchange: incomplete server params\n");
+                          return errSSLProtocol;
+                       }
+                       break;
+               case SSL_ECDH_ECDSA:
+               case SSL_ECDH_RSA:
+               {
+                       /* No server key exchange; we have to get the curve from the key */
+                       if(ctx->peerPubKey == NULL) {
+                          sslErrorLog("SSLGenClientECDHKeyAndExchange: no peer key\n");
+                          return errSSLInternal;
+                       }
+
+                       /* The peer curve is in the key's CSSM_X509_ALGORITHM_IDENTIFIER... */
+                       ortn = sslEcdsaPeerCurve(ctx->peerPubKey, &ctx->ecdhPeerCurve);
+                       if(ortn) {
+                               return ortn;
+                       }
+                       sslEcdsaDebug("SSLGenClientECDHKeyAndExchange: derived peerCurve %u",
+                               (unsigned)ctx->ecdhPeerCurve);
+                       break;
+               }
+               default:
+                       /* shouldn't be here */
+                       assert(0);
+                       return errSSLInternal;
+       }
+
+    /* Generate our (ephemeral) pair, or extract it from our signing identity */
+       if((ctx->negAuthType == SSLClientAuth_RSAFixedECDH) ||
+          (ctx->negAuthType == SSLClientAuth_ECDSAFixedECDH)) {
+               /*
+                * Client auth with a fixed ECDH key in the cert. Convert private key
+                * from SecKeyRef to CSSM format. We don't need ecdhExchangePublic
+                * because the server gets that from our cert.
+                */
+               assert(ctx->signingPrivKeyRef != NULL);
+#if USE_CDSA_CRYPTO
+               //assert(ctx->cspHand != 0);
+               sslFreeKey(ctx->cspHand, &ctx->ecdhPrivate, NULL);
+               SSLFreeBuffer(&ctx->ecdhExchangePublic, ctx);
+               ortn = SecKeyGetCSSMKey(ctx->signingPrivKeyRef, (const CSSM_KEY **)&ctx->ecdhPrivate);
+               if(ortn) {
+                       return ortn;
+               }
+               ortn = SecKeyGetCSPHandle(ctx->signingPrivKeyRef, &ctx->ecdhPrivCspHand);
+               if(ortn) {
+                       sslErrorLog("SSLGenClientECDHKeyAndExchange: SecKeyGetCSPHandle err %d\n",
+                               (int)ortn);
+               }
+#endif
+               sslEcdsaDebug("+++ Extracted ECDH private key");
+       }
+       else {
+               /* generate a new pair */
+               ortn = sslEcdhGenerateKeyPair(ctx, ctx->ecdhPeerCurve);
+               if(ortn) {
+                       return ortn;
+               }
+#if USE_CDSA_CRYPTO
+               sslEcdsaDebug("+++ Generated %u bit (%u byte) ECDH key pair",
+                       (unsigned)ctx->ecdhPrivate->KeyHeader.LogicalKeySizeInBits,
+                       (unsigned)((ctx->ecdhPrivate->KeyHeader.LogicalKeySizeInBits + 7) / 8));
+#endif
+       }
+
+
+       /* do the exchange --> premaster secret */
+       ortn = sslEcdhKeyExchange(ctx, &ctx->preMasterSecret);
+       if(ortn) {
+               return ortn;
+       }
+       return noErr;
+}
+
+
+/*
+ * Decode ECDH params and server public key.
+ */
+static OSStatus
+SSLDecodeECDHKeyParams(
+       SSLContext *ctx,
+       uint8_t **charPtr,              // IN/OUT
+       size_t length)
+{
+       OSStatus        err = noErr;
+
+       sslEcdsaDebug("+++ Decoding ECDH Server Key Exchange");
+
+       assert(ctx->protocolSide == kSSLClientSide);
+    uint8_t *endCp = *charPtr + length;
+
+       /* Allow reuse via renegotiation */
+       SSLFreeBuffer(&ctx->ecdhPeerPublic, ctx);
+
+       /*** ECParameters - just a curveType and a named curve ***/
+
+       /* 1-byte curveType, we only allow one type */
+       uint8_t curveType = **charPtr;
+       if(curveType != SSL_CurveTypeNamed) {
+               sslEcdsaDebug("+++ SSLDecodeECDHKeyParams: Bad curveType (%u)\n", (unsigned)curveType);
+               return errSSLProtocol;
+       }
+       (*charPtr)++;
+       if(*charPtr > endCp) {
+               return errSSLProtocol;
+       }
+
+       /* two-byte curve */
+       ctx->ecdhPeerCurve = SSLDecodeInt(*charPtr, 2);
+       (*charPtr) += 2;
+       if(*charPtr > endCp) {
+               return errSSLProtocol;
+       }
+       switch(ctx->ecdhPeerCurve) {
+               case SSL_Curve_secp256r1:
+               case SSL_Curve_secp384r1:
+               case SSL_Curve_secp521r1:
+                       break;
+               default:
+                       sslEcdsaDebug("+++ SSLDecodeECDHKeyParams: Bad curve (%u)\n",
+                               (unsigned)ctx->ecdhPeerCurve);
+                       return errSSLProtocol;
+       }
+
+       sslEcdsaDebug("+++ SSLDecodeECDHKeyParams: ecdhPeerCurve %u",
+               (unsigned)ctx->ecdhPeerCurve);
+
+       /*** peer public key as an ECPoint ***/
+
+       /*
+        * The spec says the the max length of an ECPoint is 255 bytes, limiting
+        * this whole mechanism to a max modulus size of 1020 bits, which I find
+        * hard to believe...
+        */
+       UInt32 len = SSLDecodeInt(*charPtr, 1);
+       (*charPtr)++;
+       if((*charPtr + len) > endCp) {
+               return errSSLProtocol;
+       }
+       err = SSLAllocBuffer(&ctx->ecdhPeerPublic, len, ctx);
+       if(err) {
+               return err;
+       }
+       memmove(ctx->ecdhPeerPublic.data, *charPtr, len);
+       (*charPtr) += len;
+
+       dumpBuf("client peer pub", &ctx->ecdhPeerPublic);
+
+       return err;
+}
+
+
+static OSStatus
+SSLEncodeECDHClientKeyExchange(SSLRecord *keyExchange, SSLContext *ctx)
+{   OSStatus            err;
+    size_t                             outputLen;
+    int                 head;
+
+       assert(ctx->protocolSide == kSSLClientSide);
+    if ((err = SSLGenClientECDHKeyAndExchange(ctx)) != 0)
+        return err;
+
+       /*
+        * Per RFC 4492 5.7, if we're doing ECDSA_fixed_ECDH or RSA_fixed_ECDH
+        * client auth, we still send this message, but it's empty (because the
+        * server gets our public key from our cert).
+        */
+       bool emptyMsg = false;
+       switch(ctx->negAuthType) {
+               case SSLClientAuth_RSAFixedECDH:
+               case SSLClientAuth_ECDSAFixedECDH:
+                       emptyMsg = true;
+                       break;
+               default:
+                       break;
+       }
+       if(emptyMsg) {
+               outputLen = 0;
+       }
+       else {
+               outputLen = ctx->ecdhExchangePublic.length + 1;
+       }
+
+    keyExchange->contentType = SSL_RecordTypeHandshake;
+       assert(ctx->negProtocolVersion >= SSL_Version_3_0);
+    keyExchange->protocolVersion = ctx->negProtocolVersion;
+    head = SSLHandshakeHeaderSize(keyExchange);
+    if ((err = SSLAllocBuffer(&keyExchange->contents,outputLen + head,ctx)) != 0)
+        return err;
+
+    uint8_t *charPtr = SSLEncodeHandshakeHeader(ctx, keyExchange, SSL_HdskClientKeyExchange, outputLen);
+       if(emptyMsg) {
+               sslEcdsaDebug("+++ Sending EMPTY ECDH Client Key Exchange");
+       }
+       else {
+               /* just a 1-byte length here... */
+               charPtr = SSLEncodeSize(charPtr, ctx->ecdhExchangePublic.length, 1);
+               memcpy(charPtr, ctx->ecdhExchangePublic.data, ctx->ecdhExchangePublic.length);
+               sslEcdsaDebug("+++ Encoded ECDH Client Key Exchange");
+       }
+
+       dumpBuf("client pub key", &ctx->ecdhExchangePublic);
+       dumpBuf("client premaster", &ctx->preMasterSecret);
+    return noErr;
+}
+
+#pragma mark -
+#pragma mark Public Functions
+OSStatus
+SSLEncodeServerKeyExchange(SSLRecord *keyExch, SSLContext *ctx)
+{   OSStatus      err;
+
+    switch (ctx->selectedCipherSpec.keyExchangeMethod)
+    {   case SSL_RSA:
+        case SSL_RSA_EXPORT:
+        #if            APPLE_DH
+               case SSL_DHE_RSA:
+               case SSL_DHE_RSA_EXPORT:
+               case SSL_DHE_DSS:
+               case SSL_DHE_DSS_EXPORT:
+               #endif  /* APPLE_DH */
+            if ((err = SSLEncodeSignedServerKeyExchange(keyExch, ctx)) != 0)
+                return err;
+            break;
+        #if            APPLE_DH
+        case SSL_DH_anon:
+               case SSL_DH_anon_EXPORT:
+            if ((err = SSLEncodeDHanonServerKeyExchange(keyExch, ctx)) != 0)
+                return err;
+            break;
+        #endif
+        default:
+            return unimpErr;
+    }
+
+    return noErr;
+}
+
+OSStatus
+SSLProcessServerKeyExchange(SSLBuffer message, SSLContext *ctx)
+{
+       OSStatus      err;
+
+    switch (ctx->selectedCipherSpec.keyExchangeMethod) {
+               case SSL_RSA:
+        case SSL_RSA_EXPORT:
+        #if            APPLE_DH
+               case SSL_DHE_RSA:
+               case SSL_DHE_RSA_EXPORT:
+               case SSL_DHE_DSS:
+               case SSL_DHE_DSS_EXPORT:
+               #endif
+               case SSL_ECDHE_ECDSA:
+               case SSL_ECDHE_RSA:
+            err = SSLDecodeSignedServerKeyExchange(message, ctx);
+            break;
+        #if            APPLE_DH
+        case SSL_DH_anon:
+               case SSL_DH_anon_EXPORT:
+            err = SSLDecodeDHanonServerKeyExchange(message, ctx);
+            break;
+        #endif
+        default:
+            err = unimpErr;
+                       break;
+    }
+
+    return err;
+}
+
+OSStatus
+SSLEncodeKeyExchange(SSLRecord *keyExchange, SSLContext *ctx)
+{   OSStatus      err;
+
+       assert(ctx->protocolSide == kSSLClientSide);
+
+       switch (ctx->selectedCipherSpec.keyExchangeMethod) {
+               case SSL_RSA:
+               case SSL_RSA_EXPORT:
+                       sslDebugLog("SSLEncodeKeyExchange: RSA method\n");
+                       err = SSLEncodeRSAKeyExchange(keyExchange, ctx);
+                       break;
+#if            APPLE_DH
+               case SSL_DHE_RSA:
+               case SSL_DHE_RSA_EXPORT:
+               case SSL_DHE_DSS:
+               case SSL_DHE_DSS_EXPORT:
+               case SSL_DH_anon:
+               case SSL_DH_anon_EXPORT:
+                       sslDebugLog("SSLEncodeKeyExchange: DH method\n");
+                       err = SSLEncodeDHClientKeyExchange(keyExchange, ctx);
+                       break;
+#endif
+               case SSL_ECDH_ECDSA:
+               case SSL_ECDHE_ECDSA:
+               case SSL_ECDH_RSA:
+               case SSL_ECDHE_RSA:
+               case SSL_ECDH_anon:
+                       sslDebugLog("SSLEncodeKeyExchange: ECDH method\n");
+                       err = SSLEncodeECDHClientKeyExchange(keyExchange, ctx);
+                       break;
+               default:
+                       sslDebugLog("SSLEncodeKeyExchange: unknown method (%d)\n",
+                                       ctx->selectedCipherSpec.keyExchangeMethod);
+                       err = unimpErr;
+       }
+
+       return err;
+}
+
+OSStatus
+SSLProcessKeyExchange(SSLBuffer keyExchange, SSLContext *ctx)
+{   OSStatus      err;
+
+       switch (ctx->selectedCipherSpec.keyExchangeMethod)
+       {   case SSL_RSA:
+               case SSL_RSA_EXPORT:
+                       sslDebugLog("SSLProcessKeyExchange: processing RSA key exchange (%d)\n",
+                                       ctx->selectedCipherSpec.keyExchangeMethod);
+                       if ((err = SSLDecodeRSAKeyExchange(keyExchange, ctx)) != 0)
+                               return err;
+                       break;
+#if            APPLE_DH
+               case SSL_DH_anon:
+               case SSL_DHE_DSS:
+               case SSL_DHE_DSS_EXPORT:
+               case SSL_DHE_RSA:
+               case SSL_DHE_RSA_EXPORT:
+               case SSL_DH_anon_EXPORT:
+                       sslDebugLog("SSLProcessKeyExchange: processing DH key exchange (%d)\n",
+                                       ctx->selectedCipherSpec.keyExchangeMethod);
+                       if ((err = SSLDecodeDHClientKeyExchange(keyExchange, ctx)) != 0)
+                               return err;
+                       break;
+#endif
+               default:
+                       sslErrorLog("SSLProcessKeyExchange: unknown keyExchangeMethod (%d)\n",
+                                       ctx->selectedCipherSpec.keyExchangeMethod);
+                       return unimpErr;
+       }
+
+       return noErr;
+}
+
+OSStatus
+SSLInitPendingCiphers(SSLContext *ctx)
+{   OSStatus        err;
+    SSLBuffer       key;
+    uint8_t         *keyDataProgress, *keyPtr, *ivPtr;
+    int             keyDataLen;
+    CipherContext   *serverPending, *clientPending;
+
+    key.data = 0;
+
+    ctx->readPending.macRef = ctx->selectedCipherSpec.macAlgorithm;
+    ctx->writePending.macRef = ctx->selectedCipherSpec.macAlgorithm;
+    ctx->readPending.symCipher = ctx->selectedCipherSpec.cipher;
+    ctx->writePending.symCipher = ctx->selectedCipherSpec.cipher;
+
+    if(ctx->negProtocolVersion == DTLS_Version_1_0)
+    {
+        ctx->readPending.sequenceNum.high = (ctx->readPending.sequenceNum.high & (0xffff<<16)) + (1<<16);
+        ctx->writePending.sequenceNum.high = (ctx->writePending.sequenceNum.high & (0xffff<<16)) + (1<<16);
+    } else {
+        ctx->writePending.sequenceNum.high=0;
+        ctx->readPending.sequenceNum.high=0;
+    }
+    ctx->readPending.sequenceNum.low = 0;
+    ctx->writePending.sequenceNum.low = 0;
+
+    keyDataLen = ctx->selectedCipherSpec.macAlgorithm->hash->digestSize +
+                     ctx->selectedCipherSpec.cipher->secretKeySize;
+    if (ctx->selectedCipherSpec.isExportable == NotExportable)
+        keyDataLen += ctx->selectedCipherSpec.cipher->ivSize;
+    keyDataLen *= 2;        /* two of everything */
+
+    if ((err = SSLAllocBuffer(&key, keyDataLen, ctx)) != 0)
+        return err;
+       assert(ctx->sslTslCalls != NULL);
+    if ((err = ctx->sslTslCalls->generateKeyMaterial(key, ctx)) != 0)
+        goto fail;
+
+    if (ctx->protocolSide == kSSLServerSide)
+    {   serverPending = &ctx->writePending;
+        clientPending = &ctx->readPending;
+    }
+    else
+    {   serverPending = &ctx->readPending;
+        clientPending = &ctx->writePending;
+    }
+
+    keyDataProgress = key.data;
+    memcpy(clientPending->macSecret, keyDataProgress,
+               ctx->selectedCipherSpec.macAlgorithm->hash->digestSize);
+    keyDataProgress += ctx->selectedCipherSpec.macAlgorithm->hash->digestSize;
+    memcpy(serverPending->macSecret, keyDataProgress,
+               ctx->selectedCipherSpec.macAlgorithm->hash->digestSize);
+    keyDataProgress += ctx->selectedCipherSpec.macAlgorithm->hash->digestSize;
+
+       /* init the reusable-per-record MAC contexts */
+       err = ctx->sslTslCalls->initMac(clientPending, ctx);
+       if(err) {
+               goto fail;
+       }
+       err = ctx->sslTslCalls->initMac(serverPending, ctx);
+       if(err) {
+               goto fail;
+       }
+
+    if (ctx->selectedCipherSpec.isExportable == NotExportable)
+    {   keyPtr = keyDataProgress;
+        keyDataProgress += ctx->selectedCipherSpec.cipher->secretKeySize;
+        /* Skip server write key to get to IV */
+               UInt8 ivSize = ctx->selectedCipherSpec.cipher->ivSize;
+
+               if (ivSize == 0)
+               {
+                       ivPtr = NULL;
+               }
+               else
+               {
+                       ivPtr = keyDataProgress + ctx->selectedCipherSpec.cipher->secretKeySize;
+               }
+
+        if ((err = ctx->selectedCipherSpec.cipher->initialize(keyPtr, ivPtr,
+                                    clientPending, ctx)) != 0)
+            goto fail;
+        keyPtr = keyDataProgress;
+        keyDataProgress += ctx->selectedCipherSpec.cipher->secretKeySize;
+        /* Skip client write IV to get to server write IV */
+               if (ivSize == 0)
+               {
+                       ivPtr = NULL;
+               }
+               else
+               {
+                       ivPtr = keyDataProgress + ctx->selectedCipherSpec.cipher->ivSize;
+               }
+
+        if ((err = ctx->selectedCipherSpec.cipher->initialize(keyPtr, ivPtr,
+                                    serverPending, ctx)) != 0)
+            goto fail;
+    }
+    else {
+        uint8_t                clientExportKey[16], serverExportKey[16],
+                                       clientExportIV[16],  serverExportIV[16];
+        SSLBuffer   clientWrite, serverWrite;
+        SSLBuffer      finalClientWrite, finalServerWrite;
+               SSLBuffer       finalClientIV, finalServerIV;
+
+        assert(ctx->selectedCipherSpec.cipher->keySize <= 16);
+        assert(ctx->selectedCipherSpec.cipher->ivSize <= 16);
+
+               /* Inputs to generateExportKeyAndIv are clientRandom, serverRandom,
+                *    clientWriteKey, serverWriteKey. The first two are already present
+                *    in ctx.
+                * Outputs are a key and IV for each of {server, client}.
+                */
+        clientWrite.data = keyDataProgress;
+        clientWrite.length = ctx->selectedCipherSpec.cipher->secretKeySize;
+        serverWrite.data = keyDataProgress + clientWrite.length;
+        serverWrite.length = ctx->selectedCipherSpec.cipher->secretKeySize;
+               finalClientWrite.data = clientExportKey;
+               finalServerWrite.data   = serverExportKey;
+               finalClientIV.data      = clientExportIV;
+               finalServerIV.data      = serverExportIV;
+               finalClientWrite.length = 16;
+               finalServerWrite.length = 16;
+               /* these can be zero */
+               finalClientIV.length    = ctx->selectedCipherSpec.cipher->ivSize;
+               finalServerIV.length    = ctx->selectedCipherSpec.cipher->ivSize;
+
+               assert(ctx->sslTslCalls != NULL);
+               err = ctx->sslTslCalls->generateExportKeyAndIv(ctx, clientWrite, serverWrite,
+                       finalClientWrite, finalServerWrite, finalClientIV, finalServerIV);
+               if(err) {
+                       goto fail;
+               }
+        if ((err = ctx->selectedCipherSpec.cipher->initialize(clientExportKey,
+                               clientExportIV, clientPending, ctx)) != 0)
+            goto fail;
+        if ((err = ctx->selectedCipherSpec.cipher->initialize(serverExportKey,
+                               serverExportIV, serverPending, ctx)) != 0)
+            goto fail;
+    }
+
+       /* Ciphers are ready for use */
+    ctx->writePending.ready = 1;
+    ctx->readPending.ready = 1;
+
+       /* Ciphers get swapped by sending or receiving a change cipher spec message */
+
+    err = noErr;
+fail:
+    SSLFreeBuffer(&key, ctx);
+    return err;
+}