Milán Major

netscape-revival

1 branch
Code

security/lib/ssl/sslcon.c

/*
 * SSL stuff. XXX
 *
 * Copyright © 1995 Netscape Communications Corporation, all rights reserved.
 *
 * $Id: sslcon.c,v 1.105.2.8 1997/05/24 00:24:38 jwz Exp $
 */

#include "xp.h"
#include "cert.h"
#include "secitem.h"
#include "secrng.h"
#include "sechash.h"
#include "crypto.h"
#include "key.h"
#include "ssl.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "ssl3prot.h"
#include "sslerr.h"
#include "pk11func.h"

extern int SEC_ERROR_INVALID_ARGS;
extern int SEC_ERROR_LIBRARY_FAILURE;
extern int SEC_ERROR_BAD_DATA;
extern int SSL_ERROR_BAD_CERTIFICATE;
extern int SSL_ERROR_BAD_CLIENT;
extern int SSL_ERROR_BAD_SERVER;
extern int SSL_ERROR_EXPORT_ONLY_SERVER;
extern int SSL_ERROR_NO_CERTIFICATE;
extern int SSL_ERROR_NO_CYPHER_OVERLAP;
extern int SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE;
extern int SSL_ERROR_UNSUPPORTED_VERSION;
extern int SSL_ERROR_US_ONLY_SERVER;
extern int SSL_ERROR_SSL2_DISABLED;
extern int SSL_ERROR_SSL_DISABLED;
extern int XP_ERRNO_EIO;

static PRBool policyWasSet;

/* This ordered list is indexed by XXXXXXXXXXXXX */
static unsigned char allCipherSuites[] = {
    0,						0,    0,
    SSL_CK_RC4_128_WITH_MD5,			0x00, 0x80,
    SSL_CK_RC4_128_EXPORT40_WITH_MD5,		0x00, 0x80,
    SSL_CK_RC2_128_CBC_WITH_MD5,		0x00, 0x80,
    SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5,	0x00, 0x80,
    SSL_CK_IDEA_128_CBC_WITH_MD5,		0x00, 0x80,
    SSL_CK_DES_64_CBC_WITH_MD5,			0x00, 0x40,
    SSL_CK_DES_192_EDE3_CBC_WITH_MD5,		0x00, 0xC0,
    0,						0,    0
};

static unsigned char *preferredCipher;



/*
** Put a string tag in the library so that we can examine an executable
** and see what kind of security it supports.
*/
char *ssl_version = "SECURITY_VERSION:"
			" +us"
			" +export"
#ifdef TRACE
			" +trace"
#endif
#ifdef DEBUG
			" +debug"
#endif
			;

char *ssl_cipherName[] = {
    "unknown",
    "RC4",
    "RC4-Export",
    "RC2-CBC",
    "RC2-CBC-Export",
    "IDEA-CBC",
    "DES-CBC",
    "DES-EDE3-CBC",
#ifdef FORTEZZA
    "unknown",
    "Fortezza",
#endif
};

static unsigned char *cipherSpecs = 0;
static unsigned sizeCipherSpecs = 0;

/* bit-masks, showing which SSLv2 suites are allowed */
static unsigned int	allowedByPolicy;
static unsigned int	maybeAllowedByPolicy;
static unsigned int	chosenPreference;

/* bit values for the above two bit masks */
#define SSL_CB_RC4_128_WITH_MD5              (1 << SSL_CK_RC4_128_WITH_MD5)
#define SSL_CB_RC4_128_EXPORT40_WITH_MD5     (1 << SSL_CK_RC4_128_EXPORT40_WITH_MD5)
#define SSL_CB_RC2_128_CBC_WITH_MD5          (1 << SSL_CK_RC2_128_CBC_WITH_MD5)
#define SSL_CB_RC2_128_CBC_EXPORT40_WITH_MD5 (1 << SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)
#define SSL_CB_IDEA_128_CBC_WITH_MD5         (1 << SSL_CK_IDEA_128_CBC_WITH_MD5)
#define SSL_CB_DES_64_CBC_WITH_MD5           (1 << SSL_CK_DES_64_CBC_WITH_MD5)
#define SSL_CB_DES_192_EDE3_CBC_WITH_MD5     (1 << SSL_CK_DES_192_EDE3_CBC_WITH_MD5)

/* Must be in the USA if we can do triple DES. */
int
SSL_IsDomestic(void)
{
    if (! policyWasSet)
    	abort();
    return (0 != (allowedByPolicy & SSL_CB_DES_192_EDE3_CBC_WITH_MD5));
}

/* called for the URL "about:" (the about box) 
 * Taher says our RSA license agreement requires us to tell the world
 * what RSA ciphers we're using/shipping.  
 * Perhaps this should unconditionally list them all...
 */

unsigned long
SSL_SecurityCapabilities(void)
{
    unsigned long ret;
    unsigned int  allowed;

    if (! policyWasSet)
    	abort();

    ret = (SSL_SC_RSA | SSL_SC_MD2 | SSL_SC_MD5);

    allowed = allowedByPolicy;
    if (allowed & ( SSL_CB_RC2_128_CBC_WITH_MD5 |
		    SSL_CB_RC2_128_CBC_EXPORT40_WITH_MD5))
	ret |= SSL_SC_RC2_CBC;

    if (allowed & ( SSL_CB_RC4_128_WITH_MD5 |
    		    SSL_CB_RC4_128_EXPORT40_WITH_MD5 ))
	ret |= SSL_SC_RC4;

    if (allowed & SSL_CB_DES_64_CBC_WITH_MD5)
	ret |= SSL_SC_DES_CBC;

    if (allowed & SSL_CB_DES_192_EDE3_CBC_WITH_MD5)
	ret |= SSL_SC_DES_EDE3_CBC;

    if (allowed & SSL_CK_IDEA_128_CBC_WITH_MD5)
	ret |= SSL_SC_IDEA_CBC;

    return ret;
}


static SECStatus
ConstructCipherSpecs(SSLSocket *ss, unsigned char **cipherSpecs,
				    unsigned *sizeCipherSpecs)
{
    unsigned char *	cs	= NULL;
    unsigned int	allowed;
    unsigned int	count;
    unsigned int	kind;
    int 		ssl3_count;
    int 		final_count;
    SECStatus 		rv;

    /* First, count number of SSL2 ciphers enabled */
    count = 0;
    allowed = allowedByPolicy & chosenPreference;
    while (allowed) {
    	if (allowed & 1) 
	    ++count;
	allowed >>= 1;
    }

    /* ask SSL3 how many cipher suites it has. */
    rv = SSL3_ConstructV2CipherSpecsHack(ss, NULL, &ssl3_count);

    if (rv < 0) 
    	return rv;

    count += ssl3_count;

    /* Allocate memory to hold cipher specs */
    if (count > 0)
	cs = (unsigned char*) PORT_Alloc(count * 3);
    if (cs == NULL) 
    	return SECFailure;

    if (*cipherSpecs != NULL) {
	PORT_Free(*cipherSpecs);
    }
    *cipherSpecs = cs;
    *sizeCipherSpecs = count * 3;

    /* fill in cipher specs for SSL2 cipher suites */
    allowed = (allowedByPolicy & chosenPreference) >> 1;
    for (kind = 1; allowed; allowed >>= 1, ++kind) {
    	if (!(allowed & 1)) 
	    continue;

    	switch (kind) {
	case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 :
	case SSL_CK_RC2_128_CBC_WITH_MD5 :
	case SSL_CK_RC4_128_EXPORT40_WITH_MD5 :
	case SSL_CK_RC4_128_WITH_MD5 :
	case SSL_CK_IDEA_128_CBC_WITH_MD5 :
	    *cs++ = kind;
	    *cs++ = 0x00;
	    *cs++ = 0x80;
	    break;

	case SSL_CK_DES_64_CBC_WITH_MD5 :
	    *cs++ = kind;
	    *cs++ = 0x00;
	    *cs++ = 0x40;
	    break;

	case SSL_CK_DES_192_EDE3_CBC_WITH_MD5 :
	    *cs++ = kind;
	    *cs++ = 0x00;
	    *cs++ = 0xC0;
	    break;
    	}
    }

    /* now have SSL3 add its suites onto the end */
    rv = SSL3_ConstructV2CipherSpecsHack(ss, cs, &final_count);
    
    /* adjust for any difference between first pass and second pass */
    *sizeCipherSpecs -= (ssl3_count - final_count) * 3;

    return rv;
}

SECStatus
SSL_SetPolicy(long which, int policy)
{
    unsigned int bitMask;
    SECStatus rv = SECSuccess;
    
    if ((which >> 8) != 0xFF) {
	rv = SSL3_SetPolicy(which, policy);
	goto done;
    }

    which &= 0x000f;
    bitMask = 1 << which;

    if (policy == SSL_ALLOWED) {
	allowedByPolicy 	|= bitMask;
	maybeAllowedByPolicy 	|= bitMask;
    } else if (policy == SSL_RESTRICTED) {
    	allowedByPolicy 	&= ~bitMask;
	maybeAllowedByPolicy 	|= bitMask;
    } else {
    	allowedByPolicy 	&= ~bitMask;
    	maybeAllowedByPolicy 	&= ~bitMask;
    }

done:
    policyWasSet = PR_TRUE;
    return rv;
}

/* called from secmoz.c whenever an SSL preference changes. */
SECStatus
SSL_EnableCipher(long which, int enabled)
{
    SECStatus 	 rv 	  = SECSuccess;
    unsigned int bitMask;
    
    if ((which >> 8) != 0xFF) {
	rv = SSL3_EnableCipher(which, enabled);
	goto done;
    }
    which &= 0x000f;
    bitMask = 1 << which;

    if (enabled)
	chosenPreference |= bitMask;
    else
    	chosenPreference &= ~bitMask;

done:
    
    if (cipherSpecs != NULL) {
	PORT_Free(cipherSpecs);
	cipherSpecs = NULL;
    }

    return rv;
}


/************************************************************************/

/*
** XXX this needs to deal with SOCKS! We need to know the address of who
** XXX we were reallying trying to connect to, not the address of the socks
** XXX daemon. This is easy for the client, but hard for the server.
*/
int
ssl_GetPeerInfo(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    struct sockaddr_in sin;
    int addrlen, rv;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    /* if we've got a better idea of who we're talking to use that */
    if ((ss->peer != 0) && (ss->port != 0)) {
	ci->peer = ss->peer;
	ci->port = ss->port;
	return 0;
    }

    addrlen = sizeof(sin);
    PORT_Memset(&sin, 0, sizeof(sin));
    rv = XP_SOCK_GETPEERNAME(ss->fd, (struct sockaddr*)&sin, &addrlen);
    if (rv < 0) {
	PORT_SetError(XP_SOCK_ERRNO);
	return -1;
    }
    if (addrlen != sizeof(sin)) {
	SSL_DBG(("%d: SSL[%d]: getpeername: expected %d, got %d",
		 SSL_GETPID(), ss->fd, sizeof(sin), addrlen));
	PORT_SetError(XP_ERRNO_EIO);
	return -1;
    }
    ci->peer = sin.sin_addr.s_addr;
    ci->port = sin.sin_port;
    return 0;
}

/************************************************************************/

static SECStatus
CreateMAC(SSLSecurityInfo *sec, SECItem *readKey,
	  SECItem *writeKey, unsigned char cipherChoice)
{
    switch (cipherChoice) {

      case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
      case SSL_CK_RC2_128_CBC_WITH_MD5:
      case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
      case SSL_CK_RC4_128_WITH_MD5:
      case SSL_CK_DES_64_CBC_WITH_MD5:
      case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
	sec->hash = &SECHashObjects[HASH_AlgMD5];
	SECITEM_CopyItem(0, &sec->sendSecret, writeKey);
	SECITEM_CopyItem(0, &sec->rcvSecret, readKey);
	break;

      default:
	PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
	return SECFailure;
    }
    sec->hashcx = (*sec->hash->create)();
    if (sec->hashcx == NULL)
	return SECFailure;
    return SECSuccess;
}

/************************************************************************/

static int
GetSendBuffer(SSLSocket *ss, int len)
{
    SSLConnectInfo *ci;
    int rv;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    ci = ss->sec->ci;

    if (len < 128) {
	len = 128;
    }
    if (len > ci->sendBuf.space) {
	rv = ssl_GrowBuf(&ci->sendBuf, len);
	if (rv < 0) {
	    SSL_DBG(("%d: SSL[%d]: GetSendBuffer failed, tried to get %d bytes",
		     SSL_GETPID(), ss->fd, len));
	    return -1;
	}
    }
    return 0;
}

int
SSL_SendErrorMessage(SSLSocket *ss, int error)
{
    SSLSecurityInfo *sec;
    unsigned char msg[SSL_HL_ERROR_HBYTES];
    int rv;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;

    SSL_TRC(3, ("%d: SSL[%d]: sending error %d", SSL_GETPID(), ss->fd, error));

    msg[0] = SSL_MT_ERROR;
    msg[1] = MSB(error);
    msg[2] = LSB(error);
    rv = (*sec->send)(ss, msg, sizeof(msg), 0);
    if (rv >= 0) {
	rv = 0;
    }
    return rv;
}

static int
SendClientFinishedMessage(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    unsigned char msg[1 + SSL_CONNECTIONID_BYTES];
    int rv;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    if (ci->sentFinished == 0) {
	ci->sentFinished = 1;

	SSL_TRC(3, ("%d: SSL[%d]: sending client-finished",
		    SSL_GETPID(), ss->fd));

	msg[0] = SSL_MT_CLIENT_FINISHED;
	PORT_Memcpy(msg+1, ci->connectionID, sizeof(ci->connectionID));

	DUMP_MSG(29, (ss, msg, 1 + sizeof(ci->connectionID)));
	rv = (*sec->send)(ss, msg, 1 + sizeof(ci->connectionID), 0);
	if (rv >= 0) {
	    rv = 0;
	}
	return rv;
    }
    return 0;
}

static int
SendServerVerifyMessage(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    unsigned char *msg;
    int sendLen, rv;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    sendLen = 1 + SSL_CHALLENGE_BYTES;
    rv = GetSendBuffer(ss, sendLen);
    if (rv) {
	return rv;
    }

    msg = ci->sendBuf.buf;
    msg[0] = SSL_MT_SERVER_VERIFY;
    PORT_Memcpy(msg+1, ci->clientChallenge, SSL_CHALLENGE_BYTES);

    DUMP_MSG(29, (ss, msg, sendLen));
    rv = (*sec->send)(ss, msg, sendLen, 0);

    if (rv >= 0) {
	rv = 0;
    }
    return rv;
}

static int
SendServerFinishedMessage(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SSLSessionID *sid;
    unsigned char *msg;
    int sendLen, rv;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    if (ci->sentFinished == 0) {
	ci->sentFinished = 1;
	PORT_Assert(ci->sid != 0);
	sid = ci->sid;

	SSL_TRC(3, ("%d: SSL[%d]: sending server-finished",
		    SSL_GETPID(), ss->fd));

	sendLen = 1 + sizeof(sid->u.ssl2.sessionID);
	rv = GetSendBuffer(ss, sendLen);
	if (rv) {
	    return rv;
	}

	msg = ci->sendBuf.buf;
	msg[0] = SSL_MT_SERVER_FINISHED;
	PORT_Memcpy(msg+1, sid->u.ssl2.sessionID, sizeof(sid->u.ssl2.sessionID));

	DUMP_MSG(29, (ss, msg, sendLen));
	rv = (*sec->send)(ss, msg, sendLen, 0);

	if (rv < 0) {
	    /* If send failed, it is now a bogus  session-id */
	    (*sec->uncache)(sid);
	} else if (!ss->noCache) {
	    /* Put the sid in session-id cache, (may already be there) */
	    (*sec->cache)(sid);
	    rv = 0;
	}
	ssl_FreeSID(sid);
	ci->sid = 0;
	return rv;
    }
    return 0;
}

static int
SendSessionKeyMessage(SSLSocket *ss, int cipher, int keySize,
		      unsigned char *ca, int caLen,
		      unsigned char *ck, int ckLen,
		      unsigned char *ek, int ekLen)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    unsigned char *msg;
    int rv, sendLen;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    sendLen = SSL_HL_CLIENT_MASTER_KEY_HBYTES + ckLen + ekLen + caLen;
    rv = GetSendBuffer(ss, sendLen);
    if (rv) return rv;

    SSL_TRC(3, ("%d: SSL[%d]: sending client-session-key",
		SSL_GETPID(), ss->fd));

    msg = ci->sendBuf.buf;
    msg[0] = SSL_MT_CLIENT_MASTER_KEY;
    msg[1] = cipher;
    msg[2] = MSB(keySize);
    msg[3] = LSB(keySize);
    msg[4] = MSB(ckLen);
    msg[5] = LSB(ckLen);
    msg[6] = MSB(ekLen);
    msg[7] = LSB(ekLen);
    msg[8] = MSB(caLen);
    msg[9] = LSB(caLen);
    PORT_Memcpy(msg+SSL_HL_CLIENT_MASTER_KEY_HBYTES, ck, ckLen);
    PORT_Memcpy(msg+SSL_HL_CLIENT_MASTER_KEY_HBYTES+ckLen, ek, ekLen);
    PORT_Memcpy(msg+SSL_HL_CLIENT_MASTER_KEY_HBYTES+ckLen+ekLen, ca, caLen);

    DUMP_MSG(29, (ss, msg, sendLen));
    rv = (*sec->send)(ss, msg, sendLen, 0);
    if (rv >= 0) {
	rv = 0;
    }
    return rv;
}

static int
SendCertificateRequestMessage(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    unsigned char *msg;
    int rv, sendLen;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    sendLen = SSL_HL_REQUEST_CERTIFICATE_HBYTES + SSL_CHALLENGE_BYTES;
    rv = GetSendBuffer(ss, sendLen);
    if (rv) return rv;

    SSL_TRC(3, ("%d: SSL[%d]: sending certificate request",
		SSL_GETPID(), ss->fd));

    /* Generate random challenge for client to encrypt */
    RNG_GenerateGlobalRandomBytes(ci->serverChallenge, SSL_CHALLENGE_BYTES);

    msg = ci->sendBuf.buf;
    msg[0] = SSL_MT_REQUEST_CERTIFICATE;
    msg[1] = SSL_AT_MD5_WITH_RSA_ENCRYPTION;
    PORT_Memcpy(msg + SSL_HL_REQUEST_CERTIFICATE_HBYTES, ci->serverChallenge,
	      SSL_CHALLENGE_BYTES);

    DUMP_MSG(29, (ss, msg, sendLen));
    rv = (*sec->send)(ss, msg, sendLen, 0);
    if (rv >= 0) {
	rv = 0;
    }
    return rv;
}

static int
SendCertificateResponseMessage(SSLSocket *ss, SECItem *cert, SECItem *encCode)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    unsigned char *msg;
    int rv, sendLen;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    sendLen = SSL_HL_CLIENT_CERTIFICATE_HBYTES + encCode->len + cert->len;
    rv = GetSendBuffer(ss, sendLen);
    if (rv) return rv;

    SSL_TRC(3, ("%d: SSL[%d]: sending certificate response",
		SSL_GETPID(), ss->fd));

    msg = ci->sendBuf.buf;
    msg[0] = SSL_MT_CLIENT_CERTIFICATE;
    msg[1] = SSL_CT_X509_CERTIFICATE;/* XXX */
    msg[2] = MSB(cert->len);
    msg[3] = LSB(cert->len);
    msg[4] = MSB(encCode->len);
    msg[5] = LSB(encCode->len);
    PORT_Memcpy(msg + SSL_HL_CLIENT_CERTIFICATE_HBYTES, cert->data, cert->len);
    PORT_Memcpy(msg + SSL_HL_CLIENT_CERTIFICATE_HBYTES + cert->len,
	      encCode->data, encCode->len);

    DUMP_MSG(29, (ss, msg, sendLen));
    rv = (*sec->send)(ss, msg, sendLen, 0);
    if (rv >= 0) {
	rv = 0;
    }
    return rv;
}

/************************************************************************/

static int
GatherRecord(SSLSocket *ss)
{
    SSLConnectInfo *ci;
    int rv;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0) && (ss->gather != 0));
    ci = ss->sec->ci;

    /* See if we have a complete record */
    if (ss->version == SSL_LIBRARY_VERSION_3_0) {
	rv = ssl3_GatherHandshake(ss, 0);
    } else {
	rv = ssl_GatherRecord(ss, 0);
    }
    SSL_TRC(10, ("%d: SSL[%d]: handshake gathering, rv=%d",
		 SSL_GETPID(), ss->fd, rv));
    if (rv <= 0) {
	if (rv == -2) {
	    /* Would block, try again later */
	    SSL_TRC(10, ("%d: SSL[%d]: handshake blocked (need %d)",
			 SSL_GETPID(), ss->fd, ss->gather->remainder));
	    return rv;
	}
	if (rv == 0) {
	    /* EOF. Loser */
	    PORT_SetError(XP_ERRNO_EIO);
	    return -1;
	}
	return rv;
    }

    SSL_TRC(10, ("%d: SSL[%d]: got handshake record of %d bytes",
		 SSL_GETPID(), ss->fd, ss->gather->recordLen));
    ss->handshake = 0;
    return 0;
}

/************************************************************************/

static int
FillInSID(SSLSessionID *sid, int cipher,
	  unsigned char *keyData, int keyLen,
	  unsigned char *ca, int caLen,
	  int keyBits, int secretKeyBits)
{
    PORT_Assert(sid->references == 1);
    PORT_Assert(sid->cached == never_cached);
    PORT_Assert(sid->u.ssl2.masterKey.data == 0);
    PORT_Assert(sid->u.ssl2.cipherArg.data == 0);

    sid->version = SSL_LIBRARY_VERSION_2;

    sid->u.ssl2.cipherType = cipher;
    sid->u.ssl2.masterKey.data = (unsigned char*) PORT_Alloc(keyLen);
    if (!sid->u.ssl2.masterKey.data) {
	return -1;
    }
    PORT_Memcpy(sid->u.ssl2.masterKey.data, keyData, keyLen);
    sid->u.ssl2.masterKey.len = keyLen;
    sid->u.ssl2.keyBits = keyBits;
    sid->u.ssl2.secretKeyBits = secretKeyBits;

    if (caLen) {
	sid->u.ssl2.cipherArg.data = (unsigned char*) PORT_Alloc(caLen);
	if (!sid->u.ssl2.cipherArg.data) {
	    return -1;
	}
	sid->u.ssl2.cipherArg.len = caLen;
	PORT_Memcpy(sid->u.ssl2.cipherArg.data, ca, caLen);
    }
    return 0;
}

/*
** Construct session keys given the masterKey (tied to the session-id),
** the client's challenge and the server's nonce.
*/
static int
ProduceKeys(SSLSocket *ss, SECItem *readKey, SECItem *writeKey,
	    SECItem *masterKey, unsigned char *challenge,
	    unsigned char *nonce, int cipherType)
{
    unsigned char km[3*16];
    unsigned part, nkm = 0, nkd = 0, i, off;
    unsigned char countChar;
    int rv;

    MD5Context *cx = 0;
    readKey->data = 0;
    writeKey->data = 0;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));

    rv = 0;
    cx = MD5_NewContext();
    if (cx == NULL) {
	return 0;
    }

    switch (cipherType) {

    case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
    case SSL_CK_RC4_128_WITH_MD5:
    case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
    case SSL_CK_RC2_128_CBC_WITH_MD5:
	nkm = 2;
	nkd = 16;
	break;

    case SSL_CK_DES_64_CBC_WITH_MD5:
	nkm = 1;
	nkd = 8;
	break;


    case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
	nkm = 3;
	nkd = 24;
	break;
    }

    readKey->data = (unsigned char*) PORT_Alloc(nkd);
    if (!readKey->data) goto loser;
    readKey->len = nkd;

    writeKey->data = (unsigned char*) PORT_Alloc(nkd);
    if (!writeKey->data) 
    	goto loser;
    writeKey->len = nkd;

    /* Produce key material */
    countChar = '0';
    for (i = 0, off = 0; i < nkm; i++, off += 16) {
	MD5_Begin(cx);
	MD5_Update(cx, masterKey->data, masterKey->len);
	MD5_Update(cx, &countChar, 1);
	MD5_Update(cx, challenge, SSL_CHALLENGE_BYTES);
	MD5_Update(cx, nonce, SSL_CONNECTIONID_BYTES);
	MD5_End(cx, km+off, &part, MD5_LENGTH);
	countChar++;
    }

    /* Produce keys */
    switch (cipherType) {
    case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
    case SSL_CK_RC2_128_CBC_WITH_MD5:
	PORT_Memcpy(readKey->data, km, 16);
	PORT_Memcpy(writeKey->data, km+16, 16);
	break;

    case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
    case SSL_CK_RC4_128_WITH_MD5:
	PORT_Memcpy(readKey->data, km, 16);
	PORT_Memcpy(writeKey->data, km+16, 16);
	break;

    case SSL_CK_DES_64_CBC_WITH_MD5:
	PORT_Memcpy(readKey->data, km, 8);
	PORT_Memcpy(writeKey->data, km+8, 8);
	break;

    case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
	PORT_Memcpy(readKey->data, km, 24);
	PORT_Memcpy(writeKey->data, km+24, 24);
	break;
    }

loser:
    MD5_DestroyContext(cx, PR_TRUE);
    return rv;
}

static int
CreateSessionCypher(SSLSocket *ss, SSLSessionID *sid, PRBool isClient)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SECItem readKey, writeKey;
    SECItem *rk, *wk;
    int rv, blockSize, blockShift;
    PK11SlotInfo *slot = 0;
    CK_MECHANISM_TYPE mechanism;
    SECItem *param;

    void *readcx = 0;
    void *writecx = 0;
    readKey.data = 0;
    writeKey.data = 0;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0) && (ss->sec->ci->sid != 0));
    sec = ss->sec;
    ci = sec->ci;
    rk = isClient ? &readKey : &writeKey;
    wk = isClient ? &writeKey : &readKey;

    /* Produce the keys for this session */
    rv = ProduceKeys(ss, &readKey, &writeKey, &sid->u.ssl2.masterKey,
		     ci->clientChallenge, ci->connectionID,
		     sid->u.ssl2.cipherType);
    if (rv) goto loser;
    PRINT_BUF(7, (ss, "Session read-key: ", rk->data, rk->len));
    PRINT_BUF(7, (ss, "Session write-key: ", wk->data, wk->len));

    PORT_Memcpy(ci->readKey, readKey.data, readKey.len);
    PORT_Memcpy(ci->writeKey, writeKey.data, writeKey.len);
    ci->keySize = readKey.len;

    /* Setup the MAC */
    rv = CreateMAC(sec, rk, wk, sid->u.ssl2.cipherType);
    if (rv) 
    	goto loser;

    /* First create the session key object */
    SSL_TRC(3, ("%d: SSL[%d]: using %s", SSL_GETPID(), ss->fd,
	    ssl_cipherName[sid->u.ssl2.cipherType]));
    switch (sid->u.ssl2.cipherType) {


    case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
    case SSL_CK_RC4_128_WITH_MD5:
	blockSize = 1;
	blockShift = 0;
	mechanism = CKM_RC4;
	break;

    case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
    case SSL_CK_RC2_128_CBC_WITH_MD5:
	blockSize = 8;
	blockShift = 3;
	mechanism = CKM_RC2_CBC;
	break;

    case SSL_CK_DES_64_CBC_WITH_MD5:
	blockSize = 8;
	blockShift = 3;
	mechanism = CKM_DES_CBC;
	break;

    case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
	blockSize = 8;
	blockShift = 3;
	mechanism = CKM_DES3_CBC;
	break;

    default:
	SSL_DBG(("%d: SSL[%d]: CreateSessionCypher: unknown cipher=%d",
		 SSL_GETPID(), ss->fd, sid->u.ssl2.cipherType));
	PORT_SetError(isClient ? SSL_ERROR_BAD_SERVER : SSL_ERROR_BAD_CLIENT);
	goto loser;
    }

    /* set destructer before we call loser... */
    sec->destroy = (void (*)(void*,PRBool)) PK11_DestroyContext;
    slot = PK11_GetBestSlot(mechanism,ss->sec->getClientAuthDataArg);
    if (slot == NULL) goto loser;

    param = PK11_ParamFromIV(mechanism,&sid->u.ssl2.cipherArg);
    if (param == NULL) goto loser;
    readcx = PK11_CreateContextByRawKey(slot,mechanism, CKA_DECRYPT,
				   rk,param,ss->sec->getClientAuthDataArg);
    SECITEM_FreeItem(param,PR_TRUE);
    if (readcx == NULL) goto loser;

    /* build the client context */
    param = PK11_ParamFromIV(mechanism,&sid->u.ssl2.cipherArg);
    if (param == NULL) goto loser;
    writecx = PK11_CreateContextByRawKey(slot,mechanism, CKA_ENCRYPT,
				   wk,param,ss->sec->getClientAuthDataArg);
    SECITEM_FreeItem(param,PR_TRUE);
    if (writecx == NULL) goto loser;
    PK11_FreeSlot(slot);

    rv = 0;
    sec->enc = (SSLCipher) PK11_CipherOp;
    sec->dec = (SSLCipher) PK11_CipherOp;
    sec->readcx = (void*) readcx;
    sec->writecx = (void*) writecx;
    sec->blockSize = blockSize;
    sec->blockShift = blockShift;
    sec->cipherType = sid->u.ssl2.cipherType;
    sec->keyBits = sid->u.ssl2.keyBits;
    sec->secretKeyBits = sid->u.ssl2.secretKeyBits;
    goto done;

  loser:
    if (readcx) (*sec->destroy)(readcx, PR_TRUE);
    if (writecx) (*sec->destroy)(writecx, PR_TRUE);
    if (slot) PK11_FreeSlot(slot);
    rv = -1;

  done:
    SECITEM_ZfreeItem(rk, PR_FALSE);
    SECITEM_ZfreeItem(wk, PR_FALSE);
    return rv;
}

/*
** Setup the server ciphers given information from a CLIENT-MASTER-KEY
** message.
** 	"ss" pointer to the ssl-socket object
** 	"cipher" the cipher type to use
** 	"keyBits" the size of the final cipher key
** 	"ck" the clear-key data
** 	"ckLen" the number of bytes of clear-key data
** 	"ek" the encrypted-key data
** 	"ekLen" the number of bytes of encrypted-key data
** 	"ca" the cipher-arg data
** 	"caLen" the number of bytes of cipher-arg data
**
** The MASTER-KEY is constructed by first decrypting the encrypted-key
** data. This produces the SECRET-KEY-DATA. The MASTER-KEY is composed by
** concatenating the clear-key data with the SECRET-KEY-DATA. This code
** checks to make sure that the client didn't send us an improper amount
** of SECRET-KEY-DATA (it restricts the length of that data to match the
** spec).
*/
static int
ServerSetupSessionCypher(SSLSocket *ss, int cipher, int keyBits,
			 unsigned char *ck, int ckLen,
			 unsigned char *ek, int ekLen,
			 unsigned char *ca, int caLen)
{
    unsigned char mkbuf[SSL_MAX_MASTER_KEY_BYTES], *kk;
    SSLSecurityInfo *sec;
    SSLSessionID *sid;
    int rv, keySize;
    unsigned el1;
    unsigned char *kbuf = 0;
    int modulus;

    PORT_Assert((ss->sec != 0) && (ssl_server_key != 0));
    sec = ss->sec;
    PORT_Assert((sec->ci != 0) && (sec->ci->sid != 0));
    sid = sec->ci->sid;

    keySize = (keyBits + 7) >> 3;
    if (keySize > SSL_MAX_MASTER_KEY_BYTES) {
	/* bummer */
	SSL_DBG(("%d: SSL[%d]: keySize=%d ckLen=%d max session key size=%d",
		 SSL_GETPID(), ss->fd, keySize, ckLen,
		 SSL_MAX_MASTER_KEY_BYTES));
	PORT_SetError(SSL_ERROR_BAD_CLIENT);
	goto loser;
    }
    if ((cipher == SSL_CK_RC4_128_EXPORT40_WITH_MD5) ||
	(cipher == SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)) {
	if (ckLen != (keySize - (40>>3))) {
	    SSL_DBG(("%d: SSL[%d]: odd secret key size, keySize=%d ckLen=%d!",
		     SSL_GETPID(), ss->fd, keySize, ckLen));
	    /* Somebody tried to sneak by a strange secret key */
	    PORT_SetError(SSL_ERROR_BAD_CLIENT);
	    goto loser;
	}
    } else {
	if ( ckLen != 0 ) {
	    PORT_SetError(SSL_ERROR_BAD_CLIENT);
	    goto loser;
	}
    }

    kbuf = (unsigned char*) PORT_Alloc(ekLen);
    if (!kbuf) {
	goto loser;
    }

    /*
    ** Decrypt encrypted half of the key. Note that encrypted half has
    ** been bloated to match the modulus size of our public key using
    ** PKCS#1. keySize is the real size of the data that is interesting.
    ** NOTE: PK11_PubDecryptRaw will barf on a non-RSA key. This is
    ** desired behavior here.
    */
    rv = PK11_PubDecryptRaw(ssl_server_key, kbuf, &el1, ekLen, ek, ekLen);
    if (rv != SECSuccess) goto loser;

    modulus = PK11_GetPrivateModulusLen(ssl_server_key);
    if (modulus == -1) {
	/* Paranoia.. if the key was really bad, then PK11_pubDecryptRaw
	 * would have failed, therefore the we must assume that the card
	 * is just being a pain and not giving us the modulus... but it
	 * should be the same size as the encrypted key length, so use it
	 * and keep cranking */
	modulus = ekLen;
    }
    /* Cheaply verify that PKCS#1 was used to format the encryption block */
    kk = kbuf + modulus - (keySize - ckLen);
    if ((kbuf[0] != 0x00) || (kbuf[1] != 0x02) || (kk[-1] != 0x00)) {
	/* Tsk tsk. */
	SSL_DBG(("%d: SSL[%d]: strange encryption block",
		 SSL_GETPID(), ss->fd));
	PORT_SetError(SSL_ERROR_BAD_CLIENT);
	goto loser;
    }

    /* Make sure we're not subject to a version rollback attack. */
    if (ss->enableSSL3) {
	unsigned char threes[8] = { 0x03, 0x03, 0x03, 0x03,
				    0x03, 0x03, 0x03, 0x03 };
	
	if (PORT_Memcmp(kk - 8 - 1, threes, 8) == 0) {
	    PORT_SetError(SSL_ERROR_BAD_CLIENT);
	    goto loser;
	}
    }

    /*
    ** Construct master key out of the pieces.
    */
    if (ckLen) {
	PORT_Memcpy(mkbuf, ck, ckLen);
    }
    PORT_Memcpy(mkbuf+ckLen, kk, keySize-ckLen);

    /* Fill in session-id */
    rv = FillInSID(sid, cipher, mkbuf, keySize, ca, caLen,
		   keyBits, keyBits - (ckLen<<3));
    if (rv < 0) {
	goto loser;
    }

    /* Create session ciphers */
    rv = CreateSessionCypher(ss, sid, PR_FALSE);
    if (rv < 0) {
	goto loser;
    }

    SSL_TRC(1, ("%d: SSL[%d]: server, using %s cipher, clear=%d total=%d",
		SSL_GETPID(), ss->fd, ssl_cipherName[cipher],
		ckLen<<3, keySize<<3));
    rv = 0;
    goto done;

  loser:
    rv = -1;

  done:
    PORT_Free(kbuf);
    return rv;
}

/************************************************************************/

/*
** Rewrite the incoming cipher specs, eliminating anything in it we don't
** support.
*/
static int
QualifyCypherSpecs(SSLSocket *ss, unsigned char *cs, int csLen)
{
    unsigned char *ms, *hs;
    int i, hc;

    if (!cipherSpecs) {
	ConstructCipherSpecs(NULL, &cipherSpecs, &sizeCipherSpecs);
    }

    PRINT_BUF(10, (ss, "specs from client:", cs, csLen));
    hs = cs;
    hc = csLen / 3;
    csLen = 0;
    while (--hc >= 0) {
	for (i = 0, ms = cipherSpecs; i < sizeCipherSpecs; i += 3, ms += 3) {
	    if ((hs[0] == ms[0]) &&
		(hs[1] == ms[1]) &&
		(hs[2] == ms[2])) {
		/* Copy this cipher spec into the "keep" section */
		cs[0] = hs[0];
		cs[1] = hs[1];
		cs[2] = hs[2];
		cs += 3;
		csLen += 3;
		break;
	    }
	}
	hs += 3;
    }
    PRINT_BUF(10, (ss, "qualified specs from client:", cs-csLen, csLen));
    return csLen;
}

/*
** Pick the best cipher we can find, given the array of server cipher
** specs.
*/
static int
ChooseSessionCypher(SSLSocket *ss, int hc, unsigned char *hs, int *keyLenp)
{
    SSLConnectInfo *ci;
    int i, bestKeySize, bestRealKeySize, bestCypher, keySize, realKeySize;
    unsigned char *ms;

    unsigned char *ohs = hs;
    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    ci = ss->sec->ci;
    if (!cipherSpecs) {
	ConstructCipherSpecs(NULL, &cipherSpecs, &sizeCipherSpecs);
    }

    if (!preferredCipher) {
    	unsigned int allowed = allowedByPolicy & chosenPreference;
	preferredCipher = allCipherSuites;
	while (allowed) {
	    if (allowed & 1) 
	    	break;
	    allowed >>= 1;
	    preferredCipher += 3;
	}
    }
    /*
    ** Scan list of ciphers recieved from peer and look for a match in
    ** our list.
    */
    bestKeySize = bestRealKeySize = 0;
    bestCypher = -1;
    while (--hc >= 0) {
	for (i = 0, ms = cipherSpecs; i < sizeCipherSpecs; i += 3, ms += 3) {
	    if ((hs[0] == preferredCipher[0]) &&
		(hs[1] == preferredCipher[1]) &&
		(hs[2] == preferredCipher[2])) {
		/* Pick this cipher immediately! */
		/*
		** XXX this hack depends on key length being in bytes 1&2
		** XXX - which is not really what the spec allows!
		*/
		*keyLenp = (((hs[1] << 8) | hs[2]) + 7) >> 3;
		return hs[0];
	    }
	    if ((hs[0] == ms[0]) && (hs[1] == ms[1]) && (hs[2] == ms[2])) {
		/* Found a match */

		/* Use secret keySize to determine which cypher is best */
		/* XXX this is a hack that depends on old spec cipher-kind
		   formatting */
		realKeySize = (hs[1] << 8) | hs[2];
		switch (hs[0]) {
		  case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
		  case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
		    keySize = 40;
		    break;
		  default:
		    keySize = realKeySize;
		    break;
		}
		if (keySize > bestKeySize) {
		    bestCypher = hs[0];
		    bestKeySize = keySize;
		    bestRealKeySize = realKeySize;
		}
	    }
	}
	hs += 3;
    }
    if (bestCypher < 0) {
	/*
	** No overlap between server and client. Re-examine server list
	** to see what kind of ciphers it does support so that we can set
	** the error code appropriately.
	*/
	if ((ohs[0] == SSL_CK_RC4_128_WITH_MD5) ||
	    (ohs[0] == SSL_CK_RC2_128_CBC_WITH_MD5)) {
	    PORT_SetError(SSL_ERROR_US_ONLY_SERVER);
	} else if ((ohs[0] == SSL_CK_RC4_128_EXPORT40_WITH_MD5) ||
		   (ohs[0] == SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)) {
	    PORT_SetError(SSL_ERROR_EXPORT_ONLY_SERVER);
	} else {
	    PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
	}
	SSL_DBG(("%d: SSL[%d]: no cipher overlap", SSL_GETPID(), ss->fd));
	goto loser;
    }
    *keyLenp = (bestRealKeySize + 7) >> 3;
    return bestCypher;

  loser:
    return -1;
}

static int
ClientHandleServerCert(SSLSocket *ss, unsigned char *certData, int certLen)
{
    SECItem certItem;
    CERTCertificate *cert = NULL;

    PORT_Assert(ss->sec != 0);

    certItem.data = certData;
    certItem.len = certLen;

    /* decode the certificate */
    cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certItem, NULL,
				  PR_FALSE, PR_TRUE);
    
    if (cert == NULL) {
	SSL_DBG(("%d: SSL[%d]: decode of server certificate fails",
		 SSL_GETPID(), ss->fd));
	return SSL_ERROR_BAD_CERTIFICATE;
    }

#ifdef TRACE
    {
	if (ssl_trace >= 1) {
	    char *issuer;
	    char *subject;
	    issuer = CERT_NameToAscii(&cert->issuer);
	    subject = CERT_NameToAscii(&cert->subject);
	    SSL_TRC(1,("%d: server certificate issuer: '%s'",
		       SSL_GETPID(), issuer ? issuer : "OOPS"));
	    SSL_TRC(1,("%d: server name: '%s'",
		       SSL_GETPID(), subject ? subject : "OOPS"));
	    PORT_Free(issuer);
	    PORT_Free(subject);
	}
    }
#endif

    ss->sec->peerCert = cert;
    return 0;
}

/*
** Given the servers public key and cipher specs, generate a session key
** that is ready to use for encrypting/decrypting the byte stream. At
** the same time, generate the SSL_MT_CLIENT_MASTER_KEY message and
** send it to the server.
*/
static int
ClientSetupSessionCypher(SSLSocket *ss,
			 unsigned char *cs, int csLen)
{
    SSLConnectInfo *ci;
    SSLSessionID *sid;
    unsigned char *ca;
    int rv, cipher, keyLen, ckLen, caLen, nc;
    SECItem eblock, rek;
    unsigned char keyData[SSL_MAX_MASTER_KEY_BYTES];

    unsigned char iv[8];


    unsigned char *ekbuf = 0;
    CERTCertificate *cert = 0;
    SECKEYPublicKey *serverKey = 0;
    unsigned modulusLen = 0;
    eblock.data = 0;
    eblock.len = 0;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    ci = ss->sec->ci;
    PORT_Assert(ci->sid != 0);
    sid = ci->sid;

    cert = ss->sec->peerCert;
    
    serverKey = CERT_ExtractPublicKey(&cert->subjectPublicKeyInfo);
    if (!serverKey) {
	SSL_DBG(("%d: SSL[%d]: extract public key failed: error=%d",
		 SSL_GETPID(), ss->fd, PORT_GetError()));
	rv = SSL_ERROR_BAD_CERTIFICATE;
	goto loser2;
    }

    /* Choose a compatible cipher with the server */
    nc = csLen / 3;
    cipher = ChooseSessionCypher(ss, nc, cs, &keyLen);
    if (cipher < 0) {
	goto loser;
    }

    /* Generate the random keys */
    RNG_GenerateGlobalRandomBytes(keyData, sizeof(keyData));

    /*
    ** Next, carve up the keys into clear and encrypted portions. The
    ** clear data is taken from the start of keyData and the encrypted
    ** portion from the remainder. Note that each of these portions is
    ** carved in half, one half for the read-key and one for the
    ** write-key.
    */
    ca = 0;
    caLen = 0;
    switch (cipher) {

    case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
	ckLen = keyLen - (40>>3);
	RNG_GenerateGlobalRandomBytes(iv, 8);
	ca = iv;
	caLen = 8;
	break;

    case SSL_CK_RC2_128_CBC_WITH_MD5:
	ckLen = 0;
	RNG_GenerateGlobalRandomBytes(iv, 8);
	ca = iv;
	caLen = 8;
	break;

    case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
	ckLen = keyLen - (40>>3);
	break;

    case SSL_CK_RC4_128_WITH_MD5:
	ckLen = 0;
	break;

    case SSL_CK_DES_64_CBC_WITH_MD5:
    case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
	ckLen = 0;
	RNG_GenerateGlobalRandomBytes(iv, 8);
	ca = iv;
	caLen = 8;
	break;

    default:
	SSL_DBG(("%d: SSL[%d]: ClientSetupSessionCypher: unknown cipher=%d",
		 SSL_GETPID(), ss->fd, cipher));
	PORT_SetError(SSL_ERROR_BAD_SERVER);
	goto loser;
    }

    /* Fill in session-id */
    rv = FillInSID(sid, cipher, keyData, keyLen,
		   ca, caLen, keyLen << 3, (keyLen - ckLen) << 3);
    if (rv < 0) {
	goto loser;
    }

    SSL_TRC(1, ("%d: SSL[%d]: client, using %s cipher, clear=%d total=%d",
		SSL_GETPID(), ss->fd, ssl_cipherName[cipher],
		ckLen<<3, keyLen<<3));

    /* Now setup read and write ciphers */
    rv = CreateSessionCypher(ss, sid, PR_TRUE);
    if (rv < 0) {
	goto loser;
    }

    /*
    ** Fill in the encryption buffer with some random bytes. Then 
    ** copy in the portion of the session key we are encrypting.
    */
    modulusLen = SECKEY_PublicKeyStrength(serverKey);
    rek.data = keyData + ckLen;
    rek.len = keyLen - ckLen;
    rv = RSA_FormatBlock(&eblock, modulusLen, RSA_BlockPublic, &rek);
    if (rv) goto loser;
    /* Set up the padding for version 2 rollback detection. */
    /* XXX We should really use defines here */
    if (ss->enableSSL3) {
	PORT_Assert((modulusLen - rek.len) > 12);
	PORT_Memset(eblock.data + modulusLen - rek.len - 8 - 1, 0x03, 8);
    }
    ekbuf = (unsigned char*) PORT_Alloc(modulusLen);
    if (!ekbuf) goto loser;
    PRINT_BUF(10, (ss, "master key encryption block:",
		   eblock.data, eblock.len));

    /* Encrypt ekitem */
    rv = PK11_PubEncryptRaw(serverKey, ekbuf, eblock.data, modulusLen,
						ss->sec->getClientAuthDataArg);
    if (rv) goto loser;

    if (eblock.len != modulusLen) {
	/* Something strange just happened */
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
	goto loser;
    }

    /* GASP. Now we have everything ready to send */
    rv = SendSessionKeyMessage(ss, cipher, keyLen << 3, ca, caLen,
			       keyData, ckLen, ekbuf, modulusLen);
    if (rv < 0) {
	goto loser;
    }
    rv = 0;
    goto done;

  loser:
    rv = -1;

  loser2:
  done:
    PORT_Memset(keyData, 0, sizeof(keyData));
    PORT_ZFree(ekbuf, modulusLen);
    SECITEM_ZfreeItem(&eblock, PR_FALSE);
    SECKEY_DestroyPublicKey(serverKey);
    return rv;
}

/************************************************************************/

static void
ClientRegSessionID(SSLSocket *ss, unsigned char *s)
{
    SSLSecurityInfo *sec;
    SSLSessionID *sid;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci->sid != 0));
    sec = ss->sec;
    sid = sec->ci->sid;

    /* Record entry in nonce cache */
    if (sid->peerCert == NULL) {
	PORT_Memcpy(sid->u.ssl2.sessionID, s, sizeof(sid->u.ssl2.sessionID));
	sid->peerCert = CERT_DupCertificate(sec->peerCert);

    }
    if (!ss->noCache)
	(*sec->cache)(sid);
}

static int
TriggerNextMessage(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    int rv;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    if ((ci->requiredElements & CIS_HAVE_CERTIFICATE) &&
	!(ci->sentElements & CIS_HAVE_CERTIFICATE)) {
	ci->sentElements |= CIS_HAVE_CERTIFICATE;
	rv = SendCertificateRequestMessage(ss);
	return rv;
    }
    return 0;
}

static int
TryToFinish(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    char e, ef;
    int rv;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    e = ci->elements;
    ef = e | CIS_HAVE_FINISHED;
    if ((ef & ci->requiredElements) == ci->requiredElements) {
	if (sec->isServer) {
	    /* Send server finished message if we already didn't */
	    rv = SendServerFinishedMessage(ss);
	} else {
	    /* Send client finished message if we already didn't */
	    rv = SendClientFinishedMessage(ss);
	}
	if (rv) {
	    return rv;
	}
	if ((e & ci->requiredElements) == ci->requiredElements) {
	    /* Totally finished */
	    ss->handshake = 0;
	    return 0;
	}
    }
    return 0;
}

static SECStatus
SignResponse(SSLSocket *ss,
	     SECKEYPrivateKey *key,
	     SECItem *response)
{
    SGNContext *sgn = NULL;
    SSLConnectInfo *ci;
    SSLSecurityInfo *sec;
    SECStatus rv;
    unsigned char *challenge;
    unsigned int len;
    
    sec = ss->sec;
    ci = sec->ci;
    challenge = ci->serverChallenge;
    len = ci->serverChallengeLen;
    
    /* Sign the expected data... */
    sgn = SGN_NewContext(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,key);
    if (!sgn) goto loser;
    rv = SGN_Begin(sgn);
    if (rv) goto loser;
    rv = SGN_Update(sgn, ci->readKey, ci->keySize);
    if (rv) goto loser;
    rv = SGN_Update(sgn, ci->writeKey, ci->keySize);
    if (rv) goto loser;
    rv = SGN_Update(sgn, challenge, len);
    if (rv) goto loser;
    rv = SGN_Update(sgn,
		    sec->peerCert->derCert.data, sec->peerCert->derCert.len);
    if (rv) goto loser;
    rv = SGN_End(sgn, response);
    if (rv) goto loser;

    SGN_DestroyContext(sgn, PR_TRUE);
    return(SECSuccess);

loser:
    SGN_DestroyContext(sgn, PR_TRUE);
    return(SECFailure);
}

/*
** Try to handle a request-certificate message. Get client's certificate
** and private key and sign a message for the server to see.
*/
static int
HandleRequestCertificate(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SECStatus rv;
    SECItem response;
    int ret;
    CERTCertificate *cert;
    SECKEYPrivateKey *key;
    unsigned char authType;


    /*
     * These things all need to be initialized before we can "goto loser".
     */
    cert = NULL;
    key = NULL;
    response.data = NULL;
    ret = 0;


    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    /* get challenge info from connectionInfo */
    authType = ci->authType;

    if (authType != SSL_AT_MD5_WITH_RSA_ENCRYPTION) {
	SSL_TRC(7, ("%d: SSL[%d]: unsupported auth type 0x%x",
		    SSL_GETPID(), ss->fd, authType));
	goto no_cert_error;
    }

    /* Get certificate and private-key from client */
    if (!sec->getClientAuthData) {
	SSL_TRC(7, ("%d: SSL[%d]: client doesn't support client-auth",
		    SSL_GETPID(), ss->fd));
	goto no_cert_error;
    }
    ret = (*sec->getClientAuthData)(sec->getClientAuthDataArg, ss->fd, NULL,
				    &cert, &key);
    if ( ret == -2 ) {
	goto done;
    }

    if (ret) {
	goto no_cert_error;
    }

    rv = SignResponse(ss, key, &response);
    if ( rv != SECSuccess ) {
	ret = -1;
	goto loser;
    }
    
    /* Send response message */
    ret = SendCertificateResponseMessage(ss, &cert->derCert, &response);
    goto done;

  no_cert_error:
    SSL_TRC(7, ("%d: SSL[%d]: no certificate (ret=%d)",
		SSL_GETPID(), ss->fd, ret));
    ret = SSL_SendErrorMessage(ss, SSL_PE_NO_CERTIFICATE);

  loser:
  done:
    if ( cert ) {
	CERT_DestroyCertificate(cert);
    }
    if ( key ) {
	SECKEY_DestroyPrivateKey(key);
    }
    if ( response.data ) {
	PORT_Free(response.data);
    }
    
    return ret;
}

static SECStatus
HandleClientCertificate(SSLSocket *ss, unsigned char certType,
			unsigned char *cd, unsigned cdLen,
			unsigned char *response,
			unsigned responseLen)
{
    SECItem certItem, rep;
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SECStatus rv;
    CERTCertificate *cert;
    SECKEYPublicKey *pubKey;
    VFYContext *vfy;


    /*
     * These things all need to be initialized before we can "goto loser".
     */
    rv = SECFailure;
    cert = NULL;
    pubKey = NULL;
    vfy = NULL;

    sec = ss->sec;
    ci = sec->ci;

    /* Extract the certificate */
    certItem.data = cd;
    certItem.len = cdLen;

    cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certItem, NULL,
			 	  PR_FALSE, PR_TRUE);
    if (cert == NULL) {
	goto loser;
    }

    /* save the certificate, since the auth routine will need it */
    sec->peerCert = cert;

    /* Extract the public key */
    pubKey = CERT_ExtractPublicKey(&cert->subjectPublicKeyInfo);
    if (!pubKey) goto loser;
    
    /* Verify the response data... */
    rep.data = response;
    rep.len = responseLen;
    vfy = VFY_CreateContext(pubKey, &rep, sec->getClientAuthData);
    if (!vfy) goto loser;
    rv = VFY_Begin(vfy);
    if (rv) goto loser;
    /*
    ** XXX This is wrong! should use key material as per spec. However,
    ** XXX it happens to work for DES.
    */
    rv = VFY_Update(vfy, ci->readKey, ci->keySize);
    if (rv) goto loser;
    rv = VFY_Update(vfy, ci->writeKey, ci->keySize);
    if (rv) goto loser;
    rv = VFY_Update(vfy, ci->serverChallenge, SSL_CHALLENGE_BYTES);
    if (rv) goto loser;
    rv = VFY_Update(vfy, ssl_server_signed_certificate.data,
		    ssl_server_signed_certificate.len);
    if (rv) goto loser;
    rv = VFY_End(vfy);
    if (rv) goto loser;

    /* Now ask application if it likes the certificate... */
    rv = (SECStatus) (*sec->authCertificate)(sec->authCertificateArg,
					    ss->fd, PR_TRUE, PR_TRUE);

    if (rv) goto loser;

    /* Hey, we liked it. Make a copy for SecurityStatus call... */
    if (rv) goto loser;
    goto done;

  loser:
    sec->peerCert = NULL;
    CERT_DestroyCertificate(cert);

  done:
    VFY_DestroyContext(vfy, PR_TRUE);
    SECKEY_DestroyPublicKey(pubKey);
    return (SECStatus) rv;
}

/*
** Handle remaining messages between client/server. Process finished
** messages from either side and any authentication requests.
*/
static int
HandleMessage(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SSLGather *gs;
    unsigned char *data, *cid;
    unsigned len, certType, certLen, responseLen;
    int rv, rv2;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0) && (ss->gather != 0));
    sec = ss->sec;
    gs = ss->gather;
    ci = sec->ci;

    data = gs->buf.buf + gs->recordOffset;

    if (gs->recordLen < 1) {
	goto bad_peer;
    }
    SSL_TRC(3, ("%d: SSL[%d]: received %d message",
		SSL_GETPID(), ss->fd, data[0]));
    DUMP_MSG(29, (ss, data, gs->recordLen));

    switch (data[0]) {
      case SSL_MT_CLIENT_FINISHED:
	if (ci->elements & CIS_HAVE_FINISHED) {
	    SSL_DBG(("%d: SSL[%d]: dup client-finished message",
		     SSL_GETPID(), ss->fd));
	    goto bad_peer;
	}

	/* See if nonce matches */
	len = gs->recordLen - 1;
	cid = data + 1;
	if ((len != sizeof(ci->connectionID)) ||
	    (PORT_Memcmp(ci->connectionID, cid, len) != 0)) {
	    SSL_DBG(("%d: SSL[%d]: bad connection-id", SSL_GETPID(), ss->fd));
	    PRINT_BUF(5, (ss, "sent connection-id",
			  ci->connectionID, sizeof(ci->connectionID)));
	    PRINT_BUF(5, (ss, "rcvd connection-id", cid, len));
	    goto bad_peer;
	}

	SSL_TRC(5, ("%d: SSL[%d]: got client finished, waiting for 0x%d",
		    SSL_GETPID(), ss->fd, ci->requiredElements ^ ci->elements));
	ci->elements |= CIS_HAVE_FINISHED;
	break;

      case SSL_MT_SERVER_FINISHED:
	if (ci->elements & CIS_HAVE_FINISHED) {
	    SSL_DBG(("%d: SSL[%d]: dup server-finished message",
		     SSL_GETPID(), ss->fd));
	    goto bad_peer;
	}

	if (gs->recordLen - 1 != SSL_SESSIONID_BYTES) {
	    SSL_DBG(("%d: SSL[%d]: bad server-finished message, len=%d",
		     SSL_GETPID(), ss->fd, gs->recordLen));
	    goto bad_peer;
	}
	ClientRegSessionID(ss, data+1);
	SSL_TRC(5, ("%d: SSL[%d]: got server finished, waiting for 0x%d",
		    SSL_GETPID(), ss->fd, ci->requiredElements ^ ci->elements));
	ci->elements |= CIS_HAVE_FINISHED;
	break;

      case SSL_MT_REQUEST_CERTIFICATE:
	len = gs->recordLen - 2;
	if ((len != SSL_MIN_CHALLENGE_BYTES) ||
	    (len > SSL_MAX_CHALLENGE_BYTES)) {
	    /* Bad challenge */
	    SSL_DBG(("%d: SSL[%d]: bad cert request message: code len=%d",
		     SSL_GETPID(), ss->fd, len));
	    goto bad_peer;
	}
	
	/* save auth request info */
	ci->authType = data[1];
	ci->serverChallengeLen = len;
	PORT_Memcpy(ci->serverChallenge, data + 2, len);
	
	rv = HandleRequestCertificate(ss);
	if (rv == -2) {
	    SSL_TRC(3, ("%d: SSL[%d]: async cert request",
			SSL_GETPID(), ss->fd));
	    /* someone is handling this asynchronously */
	    return(-2);
	}
	if (rv) goto loser;
	break;

      case SSL_MT_CLIENT_CERTIFICATE:
	if (!sec->authCertificate) {
	    /* Server asked for authentication and can't handle it */
	    PORT_SetError(SSL_ERROR_BAD_SERVER);
	    goto loser;
	}
	if (gs->recordLen < SSL_HL_CLIENT_CERTIFICATE_HBYTES) goto loser;
	certType = data[1];
	certLen = (data[2] << 8) | data[3];
	responseLen = (data[4] << 8) | data[5];
	if (certType != SSL_CT_X509_CERTIFICATE) {
	    PORT_SetError(SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE);
	    goto loser;
	}
	rv = HandleClientCertificate(ss, data[1],
		data + SSL_HL_CLIENT_CERTIFICATE_HBYTES,
		certLen,
		data + SSL_HL_CLIENT_CERTIFICATE_HBYTES + certLen,
		responseLen);
	if (rv) {
	    rv2 = SSL_SendErrorMessage(ss, SSL_PE_BAD_CERTIFICATE);
	    goto loser;
	}
	ci->elements |= CIS_HAVE_CERTIFICATE;
	break;

      case SSL_MT_ERROR:
	rv = (data[1] << 8) | data[2];
	SSL_TRC(2, ("%d: SSL[%d]: got error message, error=0x%x",
		    SSL_GETPID(), ss->fd, rv));

	/* Convert protocol error number into API error number */
	switch (rv) {
	  case SSL_PE_NO_CYPHERS:
	    rv = SSL_ERROR_NO_CYPHER_OVERLAP;
	    break;
	  case SSL_PE_NO_CERTIFICATE:
	    rv = SSL_ERROR_NO_CERTIFICATE;
	    break;
	  case SSL_PE_BAD_CERTIFICATE:
	    rv = SSL_ERROR_BAD_CERTIFICATE;
	    break;
	  case SSL_PE_UNSUPPORTED_CERTIFICATE_TYPE:
	    rv = SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE;
	    break;
	  default:
	    goto bad_peer;
	}
	/* XXX make certificate-request optionally fail... */
	PORT_SetError(rv);
	goto loser;

      default:
	SSL_DBG(("%d: SSL[%d]: unknown message %d",
		 SSL_GETPID(), ss->fd, data[0]));
	goto loser;
    }

    SSL_TRC(3, ("%d: SSL[%d]: handled %d message, required=0x%x got=0x%x",
		SSL_GETPID(), ss->fd, data[0],
		ci->requiredElements, ci->elements));

    rv = TryToFinish(ss);
    if (rv) goto loser;
    if (ss->handshake == 0) {
	return 0;
    }

    ss->gather->recordLen = 0;
    ss->handshake = GatherRecord;
    ss->nextHandshake = HandleMessage;
    return TriggerNextMessage(ss);

  bad_peer:
    PORT_SetError(sec->isServer ? SSL_ERROR_BAD_CLIENT : SSL_ERROR_BAD_SERVER);
    /* FALL THROUGH */

  loser:
    return -1;
}

/************************************************************************/

static int
KeepOnGoing(SSLSocket *ss)
{
    int rv;

    rv = TryToFinish(ss);
    if (rv) return -1;
    if (ss->handshake == 0) {
	return 0;
    }
    ss->gather->recordLen = 0;
    ss->handshake = GatherRecord;
    ss->nextHandshake = HandleMessage;
    return 0;
}

static int
HandleVerifyMessage(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SSLGather *gs;
    unsigned char *data;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0) && (ss->gather != 0));
    sec = ss->sec;
    ci = sec->ci;
    gs = ss->gather;

    data = gs->buf.buf + gs->recordOffset;
    DUMP_MSG(29, (ss, data, gs->recordLen));
    if ((gs->recordLen != 1 + SSL_CHALLENGE_BYTES) ||
	(data[0] != SSL_MT_SERVER_VERIFY) ||
	PORT_Memcmp(data+1, ci->clientChallenge, SSL_CHALLENGE_BYTES)) {
	/* Bad server */
	PORT_SetError(SSL_ERROR_BAD_SERVER);
	goto loser;
    }
    ci->elements |= CIS_HAVE_VERIFY;

    SSL_TRC(5, ("%d: SSL[%d]: got server-verify, required=0x%d got=0x%x",
		SSL_GETPID(), ss->fd, ci->requiredElements, ci->elements));

    return KeepOnGoing(ss);

  loser:
    return -1;
}

int
ssl_HandleServerHelloMessage(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SSLGather *gs;
    SSLSessionID *sid;
    int rv, needed, sidHit, certLen, csLen, cidLen, certType, err;
    unsigned char *cert, *cs, *data;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0) && (ss->gather != 0));
    sec = ss->sec;
    ci = sec->ci;
    gs = ss->gather;
    PORT_Assert(ci->sid != 0);

    if (!ss->enableSSL2) {
	PORT_SetError(SSL_ERROR_SSL2_DISABLED);
	return -1;
    }

    data = gs->buf.buf + gs->recordOffset;
    DUMP_MSG(29, (ss, data, gs->recordLen));

    /* Make sure first message has some data and is the server hello message */
    if ((gs->recordLen < SSL_HL_SERVER_HELLO_HBYTES)
	|| (data[0] != SSL_MT_SERVER_HELLO)) {
	if ((data[0] == SSL_MT_ERROR) && (gs->recordLen == 3)) {
	    err = (data[1] << 8) | data[2];
	    if (err == SSL_PE_NO_CYPHERS) {
		PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
		goto loser;
	    }
	}
	goto bad_server;
    }

    sidHit = data[1];
    certType = data[2];
    ss->version = (data[3] << 8) | data[4];
    certLen = (data[5] << 8) | data[6];
    csLen = (data[7] << 8) | data[8];
    cidLen = (data[9] << 8) | data[10];
    cert = data + SSL_HL_SERVER_HELLO_HBYTES;
    cs = cert + certLen;

    SSL_TRC(5,
	    ("%d: SSL[%d]: server-hello, hit=%d vers=%x certLen=%d csLen=%d cidLen=%d",
	     SSL_GETPID(), ss->fd, sidHit, ss->version, certLen,
	     csLen, cidLen));
    if (ss->version != SSL_LIBRARY_VERSION_2) {
        if (ss->version < SSL_LIBRARY_VERSION_2) {
	  SSL_TRC(3, ("%d: SSL[%d]: demoting self (%x) to server version (%x)",
		      SSL_GETPID(), ss->fd, SSL_LIBRARY_VERSION_2,
		      ss->version));
	} else {
	  SSL_TRC(1, ("%d: SSL[%d]: server version is %x (we are %x)",
		    SSL_GETPID(), ss->fd, ss->version, SSL_LIBRARY_VERSION_2));
	  /* server claims to be newer but does not follow protocol */
	  PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
	  goto loser;
	}
    }

    /* Save connection-id for later */
    PORT_Memcpy(ci->connectionID, cs + csLen, sizeof(ci->connectionID));

    /* See if session-id hit */
    needed = CIS_HAVE_MASTER_KEY | CIS_HAVE_FINISHED | CIS_HAVE_VERIFY;
    if (sidHit) {
	if (certLen || csLen) {
	    /* Uh oh - bogus server */
	    SSL_DBG(("%d: SSL[%d]: client, huh? hit=%d certLen=%d csLen=%d",
		     SSL_GETPID(), ss->fd, sidHit, certLen, csLen));
	    goto bad_server;
	}

	/* Total winner. */
	SSL_TRC(1, ("%d: SSL[%d]: client, using nonce for peer=0x%08x "
		    "port=0x%04x",
		    SSL_GETPID(), ss->fd, ci->peer, ci->port));
	sec->peerCert = CERT_DupCertificate(ci->sid->peerCert);
	rv = CreateSessionCypher(ss, ci->sid, PR_TRUE);
	if (rv < 0) {
	    goto loser;
	}
    } else {
	if (certType != SSL_CT_X509_CERTIFICATE) {
	    PORT_SetError(SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE);
	    goto loser;
	}
	if (csLen == 0) {
	    PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
	    SSL_DBG(("%d: SSL[%d]: no cipher overlap",
		     SSL_GETPID(), ss->fd));
	    goto loser;
	}
	if (certLen == 0) {
	    SSL_DBG(("%d: SSL[%d]: client, huh? certLen=%d csLen=%d",
		     SSL_GETPID(), ss->fd, certLen, csLen));
	    goto bad_server;
	}

	sid = ci->sid;
	if (sid->cached) {
	    /* Forget our session-id - server didn't like it */
	    SSL_TRC(7, ("%d: SSL[%d]: server forgot me, uncaching session-id",
			SSL_GETPID(), ss->fd));
	    (*sec->uncache)(sid);
	    ssl_FreeSID(sid);
	    ci->sid = sid = (SSLSessionID*) PORT_ZAlloc(sizeof(SSLSessionID));
	    if (!sid) {
		goto loser;
	    }
	    sid->references = 1;
	    sid->addr = ci->peer;
	    sid->port = ci->port;
	}

	/* decode the server's certificate */
	rv = ClientHandleServerCert(ss, cert, certLen);
	if (rv < 0) {
	    if (rv == SSL_ERROR_BAD_CERTIFICATE) {
		(void) SSL_SendErrorMessage(ss, SSL_PE_BAD_CERTIFICATE);
	    }
	    goto loser;
	}

	/* Setup new session cipher */
	rv = ClientSetupSessionCypher(ss, cs, csLen);
	if (rv < 0) {
	    if (rv == SSL_ERROR_BAD_CERTIFICATE) {
		(void) SSL_SendErrorMessage(ss, SSL_PE_BAD_CERTIFICATE);
	    }
	    goto loser;
	}
    }

    /* Build up final list of required elements */
    ci->elements = CIS_HAVE_MASTER_KEY;
    ci->requiredElements = needed;

    if ( sidHit ) {
	/* verify the certificate, don't check signatures */
	rv = (int)(* ss->sec->authCertificate)(ss->sec->authCertificateArg,
					       ss->fd, PR_FALSE, PR_FALSE);
    } else {
	/* verify the certificate, check signatures */
	rv = (int)(* ss->sec->authCertificate)(ss->sec->authCertificateArg,
					       ss->fd, PR_TRUE, PR_FALSE);
    }
    
    if (rv) {
	if ( ss->sec->handleBadCert ) {
	    rv = (* ss->sec->handleBadCert )(ss->sec->badCertArg,
					     ss->fd);
	    if ( rv ) {
		if ( rv == -2 ) {
		    /* someone will handle this connection asynchronously*/
		    /* WARNING - I RETURN HERE */

		    SSL_DBG(("%d: SSL[%d]: go to async cert handler",
			     SSL_GETPID(), ss->fd));
		    return -2;
		}
		/* cert is bad */
		SSL_DBG(("%d: SSL[%d]: server certificate is no good: error=%d",
			 SSL_GETPID(), ss->fd, PORT_GetError()));
		goto loser;

	    }
	    /* cert is good */
	} else {
	    SSL_DBG(("%d: SSL[%d]: server certificate is no good: error=%d",
		     SSL_GETPID(), ss->fd, PORT_GetError()));
	    goto loser;
	}
    }

    /*
    ** At this point we have a completed session key and our session
    ** cipher is setup and ready to go. Switch to encrypted write routine
    ** as all future message data is to be encrypted.
    */
    ssl_ChooseProcs(ss);

    rv = TryToFinish(ss);
    if (rv) goto loser;
    if (ss->handshake == 0) {
	return 0;
    }

    SSL_TRC(5, ("%d: SSL[%d]: got server-hello, required=0x%d got=0x%x",
		SSL_GETPID(), ss->fd, ci->requiredElements, ci->elements));
    ss->gather->recordLen = 0;
    ss->handshake = GatherRecord;
    ss->nextHandshake = HandleVerifyMessage;
    return 0;

  bad_server:
    PORT_SetError(SSL_ERROR_BAD_SERVER);
    /* FALL THROUGH */

  loser:
    return -1;
}

int
ssl_BeginClientHandshake(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SSLSessionID *sid;
    int i, rv, sendLen, sidLen;
    unsigned char *msg, *cp;
    unsigned char *localCipherSpecs = NULL;
    unsigned localCipherSize;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;
    sec->isServer = 0;
    ssl_ChooseSessionIDProcs(sec);
    sec->sendSequence = 0;
    sec->rcvSequence = 0;

    if (!cipherSpecs && (!!ss->enableSSL2 || !!ss->enableSSL3)) {
	ConstructCipherSpecs(NULL, &cipherSpecs, &sizeCipherSpecs);
    }

    if ((!ss->enableSSL2 && !ss->enableSSL3) || !cipherSpecs) {
	SSL_DBG(("%d: SSL[%d]: Can't handshake! both v2 and v3 disabled.",
		 SSL_GETPID(), ss->fd));
	PORT_SetError(SSL_ERROR_SSL_DISABLED);
	return SECFailure;
    }
    
    /* Get peer name of server */
    rv = ssl_GetPeerInfo(ss);
    if (rv < 0) {
	goto loser;
    }

    SSL_TRC(3, ("%d: SSL[%d]: sending client-hello", SSL_GETPID(), ss->fd));

    /* Try to find server in our session-id cache */
    if (ss->noCache) {
	sid = NULL;
    } else {
	sid = ssl_LookupSID(ci->peer, ci->port, ss->peerID);
    }
    if (sid) {
	if (((sid->version == SSL_LIBRARY_VERSION_2) && !ss->enableSSL2) ||
	    ((sid->version == SSL_LIBRARY_VERSION_3_0) && !ss->enableSSL3)) {
	    sec->uncache(sid);
	    goto invalid;
	}
	if (ss->enableSSL2) {
	    if (sid->version == SSL_LIBRARY_VERSION_2) {
		for (i = 0; i < sizeCipherSpecs; i += 3) {
		    if (cipherSpecs[i] == sid->u.ssl2.cipherType)
			goto sid_cipher_match;
		}
		goto invalid;
	    }
	}
    sid_cipher_match:
	sidLen = sizeof(sid->u.ssl2.sessionID);
	PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl2.sessionID,
		      sidLen));
	ss->version = sid->version;
    } else {
    invalid:
	sidLen = 0;
	sid = (SSLSessionID*) PORT_ZAlloc(sizeof(SSLSessionID));
	if (!sid) {
	    goto loser;
	}
	sid->references = 1;
	sid->addr = ci->peer;
	sid->port = ci->port;
	if (ss->peerID == NULL) {
	    sid->peerID = NULL;
	} else {
	    sid->peerID = PORT_Strdup(ss->peerID);
	}
    }
    ci->sid = sid;

    PORT_Assert(sid != NULL);

    if (sid->version == SSL_LIBRARY_VERSION_3_0) {
	PORT_Assert(ss->gather != NULL);
	ss->gather->state = GS_INIT;
	ss->handshake = GatherRecord;
	ss->version = SSL_LIBRARY_VERSION_3_0;
	return SSL3_SendClientHello(ss);
    }
   
#ifdef FORTEZZA
    /* 
     * if SSL3 was turned off, don't use fortezza!
     */
    if (!ss->enableSSL3) {
    	if (!cipherSpecs) {
	    ConstructCipherSpecs(NULL, &cipherSpecs, &sizeCipherSpecs);
    	}
	localCipherSpecs = cipherSpecs;
	localCipherSize = sizeCipherSpecs;
    } else {
        rv = ConstructCipherSpecs(ss, &localCipherSpecs, &localCipherSize);
	if (rv < 0) return rv;
    }
#else 
    if (!cipherSpecs) {
	ConstructCipherSpecs(NULL, &cipherSpecs, &sizeCipherSpecs);
    }
    localCipherSpecs = cipherSpecs;
    localCipherSize = sizeCipherSpecs;
#endif
    sendLen = SSL_HL_CLIENT_HELLO_HBYTES + localCipherSize + sidLen +
	SSL_CHALLENGE_BYTES;
    rv = GetSendBuffer(ss, sendLen);
    if (rv) goto loser;

    /* Generate challenge bytes for server */
    RNG_GenerateGlobalRandomBytes(ci->clientChallenge, SSL_CHALLENGE_BYTES);

    /* Construct client-hello message */
    cp = msg = ci->sendBuf.buf;
    msg[0] = SSL_MT_CLIENT_HELLO;
    if ( ss->enableSSL3 ) {
	msg[1] = MSB(SSL_LIBRARY_VERSION_3_0);
	msg[2] = LSB(SSL_LIBRARY_VERSION_3_0);
    } else {
	msg[1] = MSB(SSL_LIBRARY_VERSION_2);
	msg[2] = LSB(SSL_LIBRARY_VERSION_2);
    }
    
    msg[3] = MSB(localCipherSize);
    msg[4] = LSB(localCipherSize);
    msg[5] = MSB(sidLen);
    msg[6] = LSB(sidLen);
    msg[7] = MSB(SSL_CHALLENGE_BYTES);
    msg[8] = LSB(SSL_CHALLENGE_BYTES);
    cp += SSL_HL_CLIENT_HELLO_HBYTES;
    PORT_Memcpy(cp, localCipherSpecs, localCipherSize);
    cp += localCipherSize;
    if (sidLen) {
	PORT_Memcpy(cp, sid->u.ssl2.sessionID, sidLen);
	cp += sidLen;
    }
    PORT_Memcpy(cp, ci->clientChallenge, SSL_CHALLENGE_BYTES);

    /* Send it to the server */
    DUMP_MSG(29, (ss, msg, sendLen));
    rv = (*sec->send)(ss, msg, sendLen, 0);
    if (rv < 0) {
	goto loser;
    }

    ssl3_StartHandshakeHash(ss, msg, sendLen);

    /* Setup to receive servers hello message */
    ss->gather->recordLen = 0;
    ss->handshake = GatherRecord;
    ss->nextHandshake = ssl_HandleServerHelloMessage;
    return 0;

  loser:
    return -1;
}

/************************************************************************/

static int
HandleClientSessionKeyMessage(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SSLGather *gs;
    int rv, cipher, keySize, ckLen, ekLen, caLen;
    unsigned char *data;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0) && (ss->gather != 0));
    sec = ss->sec;
    gs = ss->gather;
    ci = sec->ci;

    data = gs->buf.buf + gs->recordOffset;
    DUMP_MSG(29, (ss, data, gs->recordLen));

    if ((gs->recordLen < SSL_HL_CLIENT_MASTER_KEY_HBYTES)
	|| (data[0] != SSL_MT_CLIENT_MASTER_KEY)) {
	goto bad_client;
    }
    cipher = data[1];
    keySize = (data[2] << 8) | data[3];
    ckLen = (data[4] << 8) | data[5];
    ekLen = (data[6] << 8) | data[7];
    caLen = (data[8] << 8) | data[9];

    SSL_TRC(5, ("%d: SSL[%d]: session-key, cipher=%d keySize=%d ckLen=%d ekLen=%d caLen=%d",
		SSL_GETPID(), ss->fd, cipher, keySize, ckLen, ekLen,
		caLen));

    if (gs->recordLen < SSL_HL_CLIENT_MASTER_KEY_HBYTES + ckLen
	+ ekLen + caLen) {
	SSL_DBG(("%d: SSL[%d]: protocol size mismatch dataLen=%d",
		 SSL_GETPID(), ss->fd, gs->recordLen));
	goto bad_client;
    }

    /* Use info from client to setup session key */
    /* XXX should validate cipher&keySize are in our array */
    rv = ServerSetupSessionCypher(ss, cipher, keySize,
		data+SSL_HL_CLIENT_MASTER_KEY_HBYTES, ckLen,
		data+SSL_HL_CLIENT_MASTER_KEY_HBYTES+ckLen, ekLen,
		data+SSL_HL_CLIENT_MASTER_KEY_HBYTES+ckLen+ekLen,
		caLen);
    if (rv < 0) {
	goto loser;
    }
    ci->elements |= CIS_HAVE_MASTER_KEY;
    ssl_ChooseProcs(ss);

    /* Send server verify message now that key's are established */
    rv = SendServerVerifyMessage(ss);
    if (rv) goto loser;

    rv = TryToFinish(ss);
    if (rv) goto loser;
    if (ss->handshake == 0) {
	return 0;
    }

    SSL_TRC(5, ("%d: SSL[%d]: server: waiting for elements=0x%d",
		SSL_GETPID(), ss->fd, ci->requiredElements ^ ci->elements));
    ss->gather->recordLen = 0;
    ss->handshake = GatherRecord;
    ss->nextHandshake = HandleMessage;
    return TriggerNextMessage(ss);

  bad_client:
    PORT_SetError(SSL_ERROR_BAD_CLIENT);
    /* FALLTHROUGH */

  loser:
    return -1;
}

/*
 * attempt to restart the handshake after asynchronously handling
 * a request for the client's certificate.
 */
int
SSL_RestartHandshakeAfterCertReq(SSLSocket *ss,
				CERTCertificate *cert, SECKEYPrivateKey *key,
						CERTCertificateList *certChain)
{
    int ret;
    SECStatus rv;
    
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SECItem response;

    if (ss->version == SSL_LIBRARY_VERSION_3_0) {
	if (ss->handshake != 0) {
	    ss->handshake = GatherRecord;
	    ss->ssl3->clientCertificate = cert;
	    ss->ssl3->clientCertChain = certChain;
	    if (key == NULL) {
		(void)SSL3_SendAlert(ss, alert_warning, no_certificate);
		ss->ssl3->clientPrivateKey = NULL;
	    } else {
		ss->ssl3->clientPrivateKey = SECKEY_CopyPrivateKey(key);
	    }
	    if (ss->ssl3->hs.msgState.buf != NULL) {
		rv = SSL3_HandleRecord(ss, NULL, &ss->gather->buf);
		return rv;
	    }
	}
	return 0;
    }

    response.data = NULL;
    
    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;

    /* generate error if no cert or key */
    if ( ( cert == NULL ) || ( key == NULL ) ) {
	goto no_cert;
    }
    
    /* generate signed response to the challenge */
    rv = SignResponse(ss, key, &response);
    if ( rv != SECSuccess ) {
	goto no_cert;
    }
    
    /* Send response message */
    ret = SendCertificateResponseMessage(ss, &cert->derCert, &response);
    if (ret) {
	goto no_cert;
    }

    /* try to finish the handshake */
    ret = TryToFinish(ss);
    if (ret) {
	goto loser;
    }
    
    /* done with handshake */
    if (ss->handshake == 0) {
	ret = 0;
	goto done;
    }

    /* continue handshake */
    ss->gather->recordLen = 0;
    ss->handshake = GatherRecord;
    ss->nextHandshake = HandleMessage;
    ret = TriggerNextMessage(ss);
    goto done;
    
no_cert:
    /* no cert - send error */
    ret = SSL_SendErrorMessage(ss, SSL_PE_NO_CERTIFICATE);
    goto done;
    
loser:
    ret = -1;
done:
    /* free allocated data */
    if ( response.data ) {
	PORT_Free(response.data);
    }
    
    return(ret);
}

#ifdef FORTEZZA
/* restart an SSL connection that we stopped to run certificate dialogs */
SECStatus
SSL_RestartHandshakeAfterFortezza(SSLSocket *ss)
{
    int rv;
    
    /*
    ** At this point we have a completed session key and our session
    ** cipher is setup and ready to go. Switch to encrypted write routine
    ** as all future message data is to be encrypted.
    */

    if (ss->reStartType == 3) {
	rv = SSL3_SendClientHello(ss);
    } else {
	rv = ssl_BeginClientHandshake(ss);
    }
    return rv;
}
#endif

/* restart an SSL connection that we stopped to run certificate dialogs */
int
SSL_RestartHandshakeAfterServerCert(SSLSocket *ss)
{
    int rv;
    
    /*
    ** At this point we have a completed session key and our session
    ** cipher is setup and ready to go. Switch to encrypted write routine
    ** as all future message data is to be encrypted.
    */

    if (ss->version == SSL_LIBRARY_VERSION_3_0) {
	if (ss->handshake != 0) {
	    ss->handshake = GatherRecord;
	    SSL3_CleanupPeerCerts(ss->ssl3);
	    ss->sec->ci->sid->peerCert = CERT_DupCertificate(ss->sec->peerCert);

	    if (ss->ssl3->hs.msgState.buf != NULL) {
		rv = SSL3_HandleRecord(ss, NULL, &ss->gather->buf);
		return rv;
	    }
	}
	return 0;
    }
    
    ssl_ChooseProcs(ss);

    rv = TryToFinish(ss);
    if (rv) {
	return -1;
    }
    
    if (ss->handshake == 0) {
	return 0;
    }

    SSL_TRC(5, ("%d: SSL[%d]: got server-hello, required=0x%d got=0x%x",
		SSL_GETPID(), ss->fd, ss->sec->ci->requiredElements,
		ss->sec->ci->elements));
    ss->gather->recordLen = 0;

    ss->handshake = GatherRecord;
    ss->nextHandshake = HandleVerifyMessage;
    return 0;
}

/*
** Handle the initial hello message from the client
*/
int
ssl_HandleClientHelloMessage(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;
    SSLGather *gs;
    SSLSessionID *sid = NULL;
    int needed, rv, hit, csLen, sendLen, sdLen, certLen, pid;
    unsigned challengeLen;
    unsigned char *msg, *data, *cs, *sd, *cert = 0, *challenge;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0) && (ss->gather != 0));
    sec = ss->sec;
    gs = ss->gather;
    ci = sec->ci;

    data = gs->buf.buf + gs->recordOffset;
    DUMP_MSG(29, (ss, data, gs->recordLen));

    /* Make sure first message has some data and is the client hello message */
    if ((gs->recordLen < SSL_HL_CLIENT_HELLO_HBYTES)
	|| (data[0] != SSL_MT_CLIENT_HELLO)) {
	goto bad_client;
    }

    /* Get peer name of client */
    rv = ssl_GetPeerInfo(ss);
    if (rv < 0) {
	goto loser;
    }

    /* Examine version information */
    /*
     * See if this might be a V3 client hello or a V2 client hello
     * asking to use the V3 protocol
     */
    if ((data[0] == SSL_MT_CLIENT_HELLO) && (data[1] == 3) && ss->enableSSL3) {
	rv = SSL3_HandleV2ClientHello(ss, data, gs->recordLen);
	if (rv != SECFailure) {
	    ss->handshake = NULL;
	    ss->nextHandshake = GatherRecord;
	    ss->securityHandshake = NULL;
	    ss->gather->state = GS_INIT;
	    ss->version = SSL_LIBRARY_VERSION_3_0;
	    ss->sec->ci->sid->version = SSL_LIBRARY_VERSION_3_0;
	}
	return rv;
    }
    if (!ss->enableSSL2) {
	PORT_SetError(SEC_ERROR_BAD_DATA);
	return SECFailure;
    }

    /* Extract info from message */
    ss->version = (data[1] << 8) | data[2];
    if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
	ss->version = SSL_LIBRARY_VERSION_2;
    }
    
    csLen = (data[3] << 8) | data[4];
    sdLen = (data[5] << 8) | data[6];
    challengeLen = (data[7] << 8) | data[8];
    cs = data + SSL_HL_CLIENT_HELLO_HBYTES;
    sd = cs + csLen;
    challenge = sd + sdLen;
    PRINT_BUF(7, (ss, "server, client session-id value:", sd, sdLen));

    if (gs->recordLen != SSL_HL_CLIENT_HELLO_HBYTES+csLen+sdLen+challengeLen) {
	SSL_DBG(("%d: SSL[%d]: bad client hello message, len=%d should=%d",
		 SSL_GETPID(), ss->fd, gs->recordLen,
		 SSL_HL_CLIENT_HELLO_HBYTES+csLen+sdLen+challengeLen));
	goto bad_client;
    }

    SSL_TRC(3, ("%d: SSL[%d]: client version is %x",
		SSL_GETPID(), ss->fd, ss->version));
    if (ss->version != SSL_LIBRARY_VERSION_2) {
	if (ss->version > SSL_LIBRARY_VERSION_2) {
	    /*
	    ** Newer client than us. Things are ok because new clients
	    ** are required to be backwards compatible with old servers.
	    ** Change version number to our version number so that client
	    ** knows whats up.
	    */
	    ss->version = SSL_LIBRARY_VERSION_2;
	} else {
	    /*
	    ** XXX here is where server backwards compatability with old
	    ** XXX clients code belongs
	    */
	    SSL_TRC(1, ("%d: SSL[%d]: client version is %x (we are %x)",
		SSL_GETPID(), ss->fd, ss->version, SSL_LIBRARY_VERSION_2));
	    PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
	    goto loser;
	}
    }

    /* Qualify cipher specs before returning them to client */
    csLen = QualifyCypherSpecs(ss, cs, csLen);
    if (csLen == 0) {
	SSL_SendErrorMessage(ss, SSL_PE_NO_CYPHERS);
	PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
	goto loser;
    }

    /* Squirrel away the challenge for later */
    PORT_Memcpy(ci->clientChallenge, challenge, challengeLen);

    /* Examine message and see if session-id is good */
    needed = 0;
    ci->elements = 0;
    if (ss->noCache) {
	sid = NULL;
    } else if (sdLen) {
	SSL_TRC(7, ("%d: SSL[%d]: server, lookup client session-id for 0x%08x",
		    SSL_GETPID(), ss->fd, ci->peer));
	sid = (*ssl_sid_lookup)(ci->peer, sd, sdLen);
    }
    if (sid) {
	/* Got a good session-id. Short cut! */
	SSL_TRC(1, ("%d: SSL[%d]: server, using session-id for 0x%08x (age=%d)",
		    SSL_GETPID(), ss->fd, ci->peer,
		    XP_TIME() - sid->time));
	PRINT_BUF(1, (ss, "session-id value:", sd, sdLen));
	ci->sid = sid;
	ci->elements = CIS_HAVE_MASTER_KEY;
	hit = 1;
	certLen = 0;
	csLen = 0;
	rv = CreateSessionCypher(ss, sid, PR_FALSE);
	if (rv < 0) {
	    goto loser;
	}
    } else {
	SSL_TRC(7, ("%d: SSL[%d]: server, lookup nonce missed",
		    SSL_GETPID(), ss->fd));
	hit = 0;
	sid = (SSLSessionID*) PORT_ZAlloc(sizeof(SSLSessionID));
	if (!sid) {
	    goto loser;
	}
	sid->references = 1;
	sid->addr = ci->peer;
	sid->port = ci->port;

	/* Invent a session-id */
	ci->sid = sid;
	RNG_GenerateGlobalRandomBytes(
	    sid->u.ssl2.sessionID+2, SSL_SESSIONID_BYTES-2);

	pid = SSL_GETPID();
	sid->u.ssl2.sessionID[0] = MSB(pid);
	sid->u.ssl2.sessionID[1] = LSB(pid);
	cert = ssl_server_signed_certificate.data;
	certLen = ssl_server_signed_certificate.len;
    }

    /* Build up final list of required elements */
    ci->requiredElements = CIS_HAVE_MASTER_KEY | CIS_HAVE_FINISHED;
    if (ss->requestCertificate) {
	ci->requiredElements |= CIS_HAVE_CERTIFICATE;
    }
    ci->sentElements = 0;

    /* Send hello message back to client */
    sendLen = SSL_HL_SERVER_HELLO_HBYTES + certLen + csLen
	+ SSL_CONNECTIONID_BYTES;
    rv = GetSendBuffer(ss, sendLen);
    if (rv < 0) {
	goto loser;
    }

    SSL_TRC(3, ("%d: SSL[%d]: sending server-hello (%d)",
		SSL_GETPID(), ss->fd, sendLen));

    msg = ci->sendBuf.buf;
    msg[0] = SSL_MT_SERVER_HELLO;
    msg[1] = hit;
    msg[2] = SSL_CT_X509_CERTIFICATE;/* XXX */
    msg[3] = MSB(ss->version);
    msg[4] = LSB(ss->version);
    msg[5] = MSB(certLen);
    msg[6] = LSB(certLen);
    msg[7] = MSB(csLen);
    msg[8] = LSB(csLen);
    msg[9] = MSB(SSL_CONNECTIONID_BYTES);
    msg[10] = LSB(SSL_CONNECTIONID_BYTES);
    if (certLen) {
	PORT_Memcpy(msg+SSL_HL_SERVER_HELLO_HBYTES, cert, certLen);
    }
    if (csLen) {
	PORT_Memcpy(msg+SSL_HL_SERVER_HELLO_HBYTES+certLen, cs, csLen);
    }
    PORT_Memcpy(msg+SSL_HL_SERVER_HELLO_HBYTES+certLen+csLen, ci->connectionID,
	      SSL_CONNECTIONID_BYTES);

    DUMP_MSG(29, (ss, msg, sendLen));
    rv = (*sec->send)(ss, msg, sendLen, 0);
    if (rv < 0) {
	goto loser;
    }
    ss->gather->recordLen = 0;
    ss->handshake = GatherRecord;
    if (hit) {
	/* Session key is good. Go encrypted */
	ssl_ChooseProcs(ss);

	/* Send server verify message now that key's are established */
	rv = SendServerVerifyMessage(ss);
	if (rv) goto loser;

	ss->nextHandshake = HandleMessage;
	return TriggerNextMessage(ss);
    }
    ss->nextHandshake = HandleClientSessionKeyMessage;
    return 0;

  bad_client:
    PORT_SetError(SSL_ERROR_BAD_CLIENT);
    /* FALLTHROUGH */

  loser:
    SSL_TRC(10, ("%d: SSL[%d]: server, wait for client-hello lossage",
		 SSL_GETPID(), ss->fd));
    return -1;
}

int
ssl_BeginServerHandshake(SSLSocket *ss)
{
    SSLSecurityInfo *sec;
    SSLConnectInfo *ci;

    PORT_Assert((ss->sec != 0) && (ss->sec->ci != 0));
    sec = ss->sec;
    ci = sec->ci;
    sec->isServer = 1;
    ssl_ChooseSessionIDProcs(sec);
    sec->sendSequence = 0;
    sec->rcvSequence = 0;

    if (!cipherSpecs && (!!ss->enableSSL2 || !!ss->enableSSL3)) {
	ConstructCipherSpecs(NULL, &cipherSpecs, &sizeCipherSpecs);
    }

    if ((!ss->enableSSL2 && !ss->enableSSL3) || !cipherSpecs) {
	SSL_DBG(("%d: SSL[%d]: Can't handshake! both v2 and v3 disabled.",
		 SSL_GETPID(), ss->fd));
	return SECFailure;
    }

#ifndef FORTEZZA
    /* you don't need these keys if fortezza is defined  */
    if (!ssl_server_key || !ssl_server_signed_certificate.data) {
	/* Need to have these defined before you can be a secure server */
	PORT_SetError(SSL_ERROR_BAD_SERVER);
	return -1;
    }
#endif

    /*
    ** Generate connection-id. Always do this, even if things fail
    ** immediately. This way the random number generator is always
    ** rolling around, every time we get a connection.
    */
    RNG_GenerateGlobalRandomBytes(ci->connectionID, sizeof(ci->connectionID));

    ss->gather->recordLen = 0;
    ss->handshake = GatherRecord;
    ss->nextHandshake = ssl_HandleClientHelloMessage;
    return 0;
}

void
ssl_DestroyConnectInfo(SSLSecurityInfo *sec)
{
    SSLConnectInfo *ci;
    SSLSessionID *sid;

    ci = sec->ci;
    if (ci) {
	PORT_FreeBlock(ci->sendBuf.buf);
	if ((sid = ci->sid) != NULL) {
	    ssl_FreeSID(sid);
	}
	PORT_ZFree(ci, sizeof(SSLConnectInfo));
	sec->ci = NULL;
    }
}