sslKeyExchange.cpp   [plain text]


/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 * specific language governing rights and limitations under the License.
 */


/*
	File:		sslKeyExchange.c

	Contains:	Support for key exchange and server key exchange

	Written by:	Doug Mitchell

	Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.

*/

#include "sslContext.h"
#include "sslHandshake.h"
#include "sslMemory.h"
#include "sslDebug.h"
#include "sslUtils.h"
#include "appleCdsa.h"
#include "sslDigests.h"
#include "ModuleAttacher.h"
#include "sslBER.h"

#include <assert.h>
#include <string.h>

#include <Security/globalizer.h>
#include <Security/threading.h>

#pragma mark -
#pragma mark *** forward static declarations ***
static OSStatus SSLGenServerDHParamsAndKey(SSLContext *ctx);
static OSStatus SSLEncodeDHKeyParams(SSLContext *ctx, UInt8 *charPtr);
static OSStatus SSLDecodeDHKeyParams(SSLContext *ctx, UInt8 *&charPtr,
	UInt32 length);

#define DH_PARAM_DUMP		0
#if 	DH_PARAM_DUMP

static void dumpBuf(const char *name, SSLBuffer &buf)
{
	printf("%s:\n", name);
	UInt8 *cp = buf.data;
	UInt8 *endCp = cp + buf.length;
	
	do {
		for(unsigned 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 D-H parameter generator ***
/*
 * Process-wide server-supplied Diffie-Hellman parameters. 
 * This might be overridden by some API_supplied parameters
 * in the future.
 */
class ServerDhParams
{
public:
	ServerDhParams();
	~ServerDhParams();
	const SSLBuffer &prime()		{ return mPrime; }
	const SSLBuffer &generator()	{ return mGenerator; }
	const SSLBuffer &paramBlock()	{ return mParamBlock; }
	
private:
	/* these two for sending over the wire */
	SSLBuffer		mPrime;		
	SSLBuffer		mGenerator;
	/* this one for sending to the CSP at key gen time */
	SSLBuffer		mParamBlock;
};

ServerDhParams::ServerDhParams()
{
	mPrime.data = NULL;
	mPrime.length = 0;
	mGenerator.data = NULL;
	mGenerator.length = 0;
	mParamBlock.data = NULL;
	mParamBlock.length = 0;
	
	CSSM_CSP_HANDLE cspHand;
	CSSM_CL_HANDLE clHand;			// not used here, just for 
									//   attachToModules()
	CSSM_TP_HANDLE tpHand;			// ditto
	CSSM_RETURN crtn;
	
	crtn = attachToModules(&cspHand, &clHand, &tpHand);
	if(crtn) {
		MacOSError::throwMe(errSSLModuleAttach);
	}
	
	CSSM_CC_HANDLE 	ccHandle;
	CSSM_DATA cParams = {0, NULL};
	
	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
		CSSM_ALGID_DH,
		SSL_DH_DEFAULT_PRIME_SIZE,
		NULL,					// Seed
		NULL,					// Salt
		NULL,					// StartDate
		NULL,					// EndDate
		&cParams,			// Params, may be NULL
		&ccHandle);
	if(crtn) {
		stPrintCdsaError("ServerDhParams CSSM_CSP_CreateKeyGenContext", crtn);
		MacOSError::throwMe(errSSLCrypto);
	}
	
	/* explicitly generate params and save them */
	sslDhDebug("^^^generating Diffie-Hellman parameters...");
	crtn = CSSM_GenerateAlgorithmParams(ccHandle, 
		SSL_DH_DEFAULT_PRIME_SIZE, &cParams);
	if(crtn) {
		stPrintCdsaError("ServerDhParams CSSM_GenerateAlgorithmParams", crtn);
		CSSM_DeleteContext(ccHandle);
		MacOSError::throwMe(errSSLCrypto);
	}
	CSSM_TO_SSLBUF(&cParams, &mParamBlock);
	OSStatus ortn = sslDecodeDhParams(&mParamBlock, &mPrime, &mGenerator);
	if(ortn) {
		sslErrorLog("ServerDhParams: param decode error\n");
		MacOSError::throwMe(ortn);
	}
	CSSM_DeleteContext(ccHandle);
}

ServerDhParams::~ServerDhParams()
{
	sslFree(mPrime.data);
	sslFree(mGenerator.data);
	sslFree(mParamBlock.data);
}

/* the single global thing */
static ModuleNexus<ServerDhParams> serverDhParams;

#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

typedef	CSSM_KEY_PTR	SSLRSAPrivateKey;

static OSStatus
SSLEncodeRSAKeyParams(SSLBuffer *keyParams, SSLRSAPrivateKey *key, SSLContext *ctx)
{   OSStatus    err;
    SSLBuffer   modulus, exponent;
    UInt8       *charPtr;

	if(err = attachToCsp(ctx)) {
		return err;
	}
	
	/* Note currently ALL public keys are raw, obtained from the CL... */
	assert((*key)->KeyHeader.BlobType == CSSM_KEYBLOB_RAW);
	err = sslGetPubKeyBits(ctx,
		*key,
		ctx->cspHand,
		&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;
}

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) ||
		   (ctx->negProtocolVersion == TLS_Version_1_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
SSLEncodeSignedServerKeyExchange(SSLRecord &keyExch, SSLContext *ctx)
{   OSStatus        err;
    UInt8           *charPtr;
    int             outputLen;
    UInt8           hashes[SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN];
    SSLBuffer       exchangeParams,clientRandom,serverRandom,hashCtx, hash;
	UInt8			*dataToSign;
	UInt32			dataToSignLen;
	bool			isRsa = true;
    UInt32 			maxSigLen;
    UInt32	    	actSigLen;
	SSLBuffer		signature;
	const CSSM_KEY	*cssmKey;
	
    assert(ctx->protocolSide == SSL_ServerSide);
	assert(ctx->signingPubKey != NULL);
 	assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
		   (ctx->negProtocolVersion == TLS_Version_1_0));
    exchangeParams.data = 0;
    hashCtx.data = 0;
	signature.data = 0;
	
	/* 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;
			}
			UInt32 len = ctx->dhParamsPrime.length + 
				ctx->dhParamsGenerator.length + 
				ctx->dhExchangePublic.length + 6 /* 3 length fields */;
			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;
	}
	if(err) {
		goto fail;
	}
			    
	/* 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;
    
	/* preallocate a buffer for signing */
	err = SecKeyGetCSSMKey(ctx->signingPrivKeyRef, &cssmKey);
	if(err) {
		sslErrorLog("SSLEncodeSignedServerKeyExchange: SecKeyGetCSSMKey err %d\n",
			(int)err);
        goto fail;
	}
	err = sslGetMaxSigSize(cssmKey, maxSigLen);
	if(err) {
        goto fail;
	}
	err = SSLAllocBuffer(signature, maxSigLen, ctx);
	if(err) {
		goto fail;
	}
	
	err = sslRawSign(ctx,
		ctx->signingPrivKeyRef,
		dataToSign,			// one or two hashes
		dataToSignLen,
		signature.data,
		maxSigLen,
		&actSigLen);
	if(err) {
		goto fail;
	}
	assert(actSigLen <= maxSigLen);
	
	/* package it all up */
    outputLen = exchangeParams.length + 2 + actSigLen;
    keyExch.protocolVersion = ctx->negProtocolVersion;
    keyExch.contentType = SSL_RecordTypeHandshake;
    if ((err = SSLAllocBuffer(keyExch.contents, outputLen+4, ctx)) != 0)
        goto fail;
    
    charPtr = keyExch.contents.data;
    *charPtr++ = SSL_HdskServerKeyExchange;
    charPtr = SSLEncodeInt(charPtr, outputLen, 3);
    
    memcpy(charPtr, exchangeParams.data, exchangeParams.length);
    charPtr += exchangeParams.length;
    charPtr = SSLEncodeInt(charPtr, actSigLen, 2);
	memcpy(charPtr, signature.data, actSigLen);
    assert((charPtr + actSigLen) == 
		   (keyExch.contents.data + keyExch.contents.length));
    
    err = noErr;
    
fail:
    SSLFreeBuffer(hashCtx, ctx);
    SSLFreeBuffer(exchangeParams, ctx);
    SSLFreeBuffer(signature, 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;
    SSLBuffer       hashOut, hashCtx, clientRandom, serverRandom;
    UInt16          modulusLen, exponentLen, signatureLen;
    UInt8           *modulus, *exponent, *signature;
    UInt8           hashes[SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN];
    SSLBuffer       signedHashes;
 	UInt8			*dataToSign;
	UInt32			dataToSignLen;
	bool			isRsa = true;
	
	assert(ctx->protocolSide == SSL_ClientSide);
	signedHashes.data = 0;
    hashCtx.data = 0;
    
    if (message.length < 2) {
    	sslErrorLog("SSLDecodeSignedServerKeyExchange: msg len error 1\n");
        return errSSLProtocol;
    }
	
	/* first extract the key-exchange-method-specific parameters */
    UInt8 *charPtr = message.data;
	UInt8 *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 */
		default:
			assert(0);
			return errSSLInternal;
	}
	
	/* this is what's hashed */
	SSLBuffer signedParams;
	signedParams.data = message.data;
	signedParams.length = charPtr - message.data;
	
	signatureLen = SSLDecodeInt(charPtr, 2);
	charPtr += 2;
	if((charPtr + signatureLen) != endCp) {
		sslErrorLog("signedServerKeyExchange: msg len error 4\n");
		return errSSLProtocol;
	}
	signature = charPtr;
	
    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 - 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,
		ctx->peerPubKeyCsp,
		dataToSign,				/* plaintext */
		dataToSignLen,			/* plaintext length */
		signature,
		signatureLen);
	if(err) {
		sslErrorLog("SSLDecodeSignedServerKeyExchange: sslRawVerify "
			"returned %d\n", (int)err);
		goto fail;
	}
    
	/* Signature matches; now replace server key with new key */
	switch(ctx->selectedCipherSpec->keyExchangeMethod) {
		case SSL_RSA:
        case SSL_RSA_EXPORT:
		{
			SSLBuffer modBuf;
			SSLBuffer expBuf;
			
			/* first free existing peerKey */
			sslFreeKey(ctx->peerPubKeyCsp, 
				&ctx->peerPubKey,
				NULL);					/* 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,
				&ctx->peerPubKeyCsp);
			break;
		}
		case SSL_DHE_RSA:
		case SSL_DHE_RSA_EXPORT:
		case SSL_DHE_DSS:
		case SSL_DHE_DSS_EXPORT:
			break;					/* handled above */
		default:
			assert(0);				/* handled above */
	}
fail:
    SSLFreeBuffer(signedHashes, ctx);
    SSLFreeBuffer(hashCtx, ctx);
    return err;
}

static OSStatus
SSLDecodeRSAKeyExchange(SSLBuffer keyExchange, SSLContext *ctx)
{   OSStatus            err;
    UInt32        		outputLen, localKeyModulusLen;
    SSLProtocolVersion  version;
    Boolean				useEncryptKey = false;
	UInt8				*src = NULL;
	SecKeyRef			keyRef = NULL;
    const CSSM_KEY		*cssmKey;
		
	assert(ctx->protocolSide == SSL_ServerSide);
	
	#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 */
	}
	err = SecKeyGetCSSMKey(keyRef, &cssmKey);
	if(err) {
		sslErrorLog("SSLDecodeRSAKeyExchange: SecKeyGetCSSMKey err %d\n",
			(int)err);
		return err;
	}
    
	localKeyModulusLen = sslKeyLengthInBytes(cssmKey);

	/* 
	 * 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,
		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;
    UInt32        		outputLen, peerKeyModulusLen;
    UInt32				bufLen;
	UInt8				*dst;
	bool				encodeLen = false;
	
	assert(ctx->protocolSide == SSL_ClientSide);
    if ((err = SSLEncodeRSAPremasterSecret(ctx)) != 0)
        return err;
    
    keyExchange.contentType = SSL_RecordTypeHandshake;
	assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
			(ctx->negProtocolVersion == TLS_Version_1_0));
    keyExchange.protocolVersion = ctx->negProtocolVersion;
        
	peerKeyModulusLen = sslKeyLengthInBytes(ctx->peerPubKey);
	bufLen = peerKeyModulusLen + 4;
	#if 	RSA_CLIENT_KEY_ADD_LENGTH
	if(ctx->negProtocolVersion >= TLS_Version_1_0) {
		bufLen += 2;
		encodeLen = true;
	}
	#endif
    if ((err = SSLAllocBuffer(keyExchange.contents, 
		bufLen,ctx)) != 0)
    {   
        return err;
    }
	dst = keyExchange.contents.data + 4;
	if(encodeLen) {
		dst += 2;
	}
    keyExchange.contents.data[0] = SSL_HdskClientKeyExchange;
	
	/* this is the record payload length */
    SSLEncodeInt(keyExchange.contents.data + 1, bufLen - 4, 3);
	if(encodeLen) {
		/* the length of the encrypted pre_master_secret */
		SSLEncodeInt(keyExchange.contents.data + 4, 			
			peerKeyModulusLen, 2);
	}
	err = sslRsaEncrypt(ctx,
		ctx->peerPubKey,
		/* FIXME - maybe this should be ctx->cspHand */
		ctx->peerPubKeyCsp,
		ctx->preMasterSecret.data, 
		SSL_RSA_PREMASTER_SECRET_SIZE,
		dst,
		peerKeyModulusLen,
		&outputLen);
	if(err) {
		return err;
	}
    
    assert(outputLen == encodeLen ? 
		keyExchange.contents.length - 6 :
		keyExchange.contents.length - 4 );
    
    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 == SSL_ServerSide);
	
	/* 
	 * Obtain D-H parameters if we don't have them.
	 */
	if(ctx->dhParamsPrime.data == NULL) {
		assert(ctx->dhParamsGenerator.data == NULL);
		const SSLBuffer &pr = serverDhParams().prime();
		ortn = SSLCopyBuffer(pr, ctx->dhParamsPrime);
		if(ortn) {
			return ortn;
		}
		const SSLBuffer &gen = serverDhParams().generator();
		ortn = SSLCopyBuffer(gen, ctx->dhParamsGenerator);
		if(ortn) {
			return ortn;
		}
		const SSLBuffer &block = serverDhParams().paramBlock();
		ortn = SSLCopyBuffer(block, ctx->dhParamsEncoded);
		if(ortn) {
			return ortn;
		}
	}
	
	/* 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);
	return noErr;
} 

/*
 * Encode DH params and public key in caller-supplied buffer. 
 */
static OSStatus 
SSLEncodeDHKeyParams(
	SSLContext *ctx,
	UInt8 *charPtr)
{
    assert(ctx->protocolSide == SSL_ServerSide);
	assert(ctx->dhParamsPrime.data != NULL);
	assert(ctx->dhParamsGenerator.data != NULL);
	assert(ctx->dhExchangePublic.data != NULL);
	
	charPtr = SSLEncodeInt(charPtr, ctx->dhParamsPrime.length, 2);
	memcpy(charPtr, ctx->dhParamsPrime.data, ctx->dhParamsPrime.length);
	charPtr += ctx->dhParamsPrime.length;
	
	charPtr = SSLEncodeInt(charPtr, ctx->dhParamsGenerator.length, 2);
	memcpy(charPtr, ctx->dhParamsGenerator.data, 
		ctx->dhParamsGenerator.length);
	charPtr += ctx->dhParamsGenerator.length;
	
	charPtr = SSLEncodeInt(charPtr, ctx->dhExchangePublic.length, 2);
	memcpy(charPtr, ctx->dhExchangePublic.data, 
		ctx->dhExchangePublic.length);

	dumpBuf("server prime", ctx->dhParamsPrime);
	dumpBuf("server generator", ctx->dhParamsGenerator);
	dumpBuf("server pub key", ctx->dhExchangePublic);
	return noErr;
}

/*
 * Decode DH params and server public key.
 */
static OSStatus
SSLDecodeDHKeyParams(
	SSLContext *ctx,
	UInt8 *&charPtr,		// IN/OUT
	UInt32 length)
{   
	OSStatus        err = noErr;
	
	assert(ctx->protocolSide == SSL_ClientSide);
    UInt8 *endCp = charPtr + length;

	/* Allow reuse via renegotiation */
    SSLFreeBuffer(ctx->dhParamsPrime, ctx);
    SSLFreeBuffer(ctx->dhParamsGenerator, ctx);
	SSLFreeBuffer(ctx->dhPeerPublic, ctx);
	
	/* Prime, with a two-byte length */
	UInt32 len = SSLDecodeInt(charPtr, 2);
	charPtr += 2;
	if((charPtr + len) > endCp) {
		return errSSLProtocol;
	}
	err = SSLAllocBuffer(ctx->dhParamsPrime, len, ctx);
	if(err) {
		return err;
	}
	memmove(ctx->dhParamsPrime.data, charPtr, len);
	charPtr += len;
	
	/* Generator, with a two-byte length */
	len = SSLDecodeInt(charPtr, 2);
	charPtr += 2;
	if((charPtr + len) > endCp) {
		return errSSLProtocol;
	}
	err = SSLAllocBuffer(ctx->dhParamsGenerator, len, ctx);
	if(err) {
		return err;
	}
	memmove(ctx->dhParamsGenerator.data, charPtr, len);
	charPtr += len;
	
	/* 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;

    assert(ctx->protocolSide == SSL_ClientSide);
	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);
	return noErr;
}

static OSStatus
SSLEncodeDHanonServerKeyExchange(SSLRecord &keyExch, SSLContext *ctx)
{   
	OSStatus            ortn = noErr;
	
	assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
			(ctx->negProtocolVersion == TLS_Version_1_0));
	assert(ctx->protocolSide == SSL_ServerSide);

	/* 
	 * Obtain D-H parameters (if we don't have them) and a key pair. 
	 */
	ortn = SSLGenServerDHParamsAndKey(ctx);
	if(ortn) {
		return ortn;
	}
	
	UInt32 length = 6 + 
		ctx->dhParamsPrime.length + 
		ctx->dhParamsGenerator.length + ctx->dhExchangePublic.length;
	
	keyExch.protocolVersion = ctx->negProtocolVersion;
	keyExch.contentType = SSL_RecordTypeHandshake;
	if ((ortn = SSLAllocBuffer(keyExch.contents, length+4, ctx)) != 0)
		return ortn;
	
	UInt8 *charPtr = keyExch.contents.data;
	*charPtr++ = SSL_HdskServerKeyExchange;
	charPtr = SSLEncodeInt(charPtr, length, 3);
	
	/* encode prime, generator, our public key */
	return SSLEncodeDHKeyParams(ctx, charPtr);
}


static OSStatus
SSLDecodeDHanonServerKeyExchange(SSLBuffer message, SSLContext *ctx)
{   
	OSStatus        err = noErr;
	
	assert(ctx->protocolSide == SSL_ClientSide);
    if (message.length < 6) {
    	sslErrorLog("SSLDecodeDHanonServerKeyExchange error: msg len %u\n",
    		(unsigned)message.length);
        return errSSLProtocol;
    }
    UInt8 *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 == SSL_ServerSide);
	if(ctx->dhParamsPrime.data == NULL) {
		/* should never happen */
		assert(0);
		return errSSLInternal;
	}
	
	/* this message simply contains the client's public DH key */
	UInt8 *charPtr = keyExchange.data;
    publicLen = SSLDecodeInt(charPtr, 2);
	charPtr += 2;
	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);
	ortn = sslDhKeyExchange(ctx, ctx->dhParamsPrime.length * 8, 
		&ctx->preMasterSecret);

	dumpBuf("server peer pub", ctx->dhPeerPublic);
	dumpBuf("server premaster", ctx->preMasterSecret);
	return ortn;
}

static OSStatus
SSLEncodeDHClientKeyExchange(SSLRecord &keyExchange, SSLContext *ctx)
{   OSStatus            err;
    unsigned int        outputLen;
    
	assert(ctx->protocolSide == SSL_ClientSide);
    if ((err = SSLGenClientDHKeyAndExchange(ctx)) != 0)
        return err;
    
    outputLen = ctx->dhExchangePublic.length + 2;
    
    keyExchange.contentType = SSL_RecordTypeHandshake;
	assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
			(ctx->negProtocolVersion == TLS_Version_1_0));
    keyExchange.protocolVersion = ctx->negProtocolVersion;
    
    if ((err = SSLAllocBuffer(keyExchange.contents,outputLen + 4,ctx)) != 0)
        return err;
    
    keyExchange.contents.data[0] = SSL_HdskClientKeyExchange;
    SSLEncodeInt(keyExchange.contents.data+1, 
		ctx->dhExchangePublic.length+2, 3);
    
    SSLEncodeInt(keyExchange.contents.data+4, 
		ctx->dhExchangePublic.length, 2);
    memcpy(keyExchange.contents.data+6, 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 *** 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
            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 == SSL_ClientSide);
    
    switch (ctx->selectedCipherSpec->keyExchangeMethod) {
		case SSL_RSA:
        case SSL_RSA_EXPORT:
            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:
            err = SSLEncodeDHClientKeyExchange(keyExchange, ctx);
            break;
        #endif
        default:
            err = unimpErr;
    }
    
    return err;
}

OSStatus
SSLProcessKeyExchange(SSLBuffer keyExchange, SSLContext *ctx)
{   OSStatus      err;
    
    switch (ctx->selectedCipherSpec->keyExchangeMethod)
    {   case SSL_RSA:
        case SSL_RSA_EXPORT:
            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:
            if ((err = SSLDecodeDHClientKeyExchange(keyExchange, ctx)) != 0)
                return err;
            break;
        #endif
        default:
            return unimpErr;
    }
    
    return noErr;
}

OSStatus
SSLInitPendingCiphers(SSLContext *ctx)
{   OSStatus        err;
    SSLBuffer       key;
    UInt8           *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;
    ctx->readPending.sequenceNum.high = ctx->readPending.sequenceNum.low = 0;
    ctx->writePending.sequenceNum.high = 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 == SSL_ServerSide)
    {   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 */
        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 */
        ivPtr = keyDataProgress + ctx->selectedCipherSpec->cipher->ivSize;
        if ((err = ctx->selectedCipherSpec->cipher->initialize(keyPtr, ivPtr,
                                    serverPending, ctx)) != 0)
            goto fail;
    }
    else {
        UInt8		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;
}