sslKeyExchange.c   [plain text]


/*
 * 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;
}