sslHandshake.c   [plain text]


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


/*
 * sslHandshake.c - SSL 3.0 handshake state machine.
 */

#include "sslContext.h"
#include "sslHandshake.h"
#include "sslMemory.h"
#include "sslAlertMessage.h"
#include "sslSession.h"
#include "sslUtils.h"
#include "sslDebug.h"
#include "sslCrypto.h"

#include "sslDigests.h"
#include "cipherSpecs.h"

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

#define REQUEST_CERT_CORRECT        0

#if __LP64__
#define PRIstatus "d"
#else
#define PRIstatus "ld"
#endif

static OSStatus SSLProcessHandshakeMessage(SSLHandshakeMsg message, SSLContext *ctx);

static OSStatus
SSLUpdateHandshakeMacs(const SSLBuffer *messageData, SSLContext *ctx)
{
    OSStatus err = errSecParam;
    bool do_md5 = false;
    bool do_sha1 = false;
    bool do_sha256 = false;
    bool do_sha384 = false;

    //TODO: We can stop updating the unecessary hashes once the CertVerify message is processed in case where we do Client Side Auth, or .

    if(ctx->negProtocolVersion == SSL_Version_Undetermined)
    {
        // we dont know yet, so we might need MD5 & SHA1 - Server should always call in with known protocol version.
        assert(ctx->protocolSide==kSSLClientSide);
        do_md5 = do_sha1 = true;
        if(ctx->isDTLS
           ? ctx->maxProtocolVersion < DTLS_Version_1_0
           : ctx->maxProtocolVersion >= TLS_Version_1_2)
        {
            // We wil need those too, unless we are sure we wont end up doing TLS 1.2
            do_sha256 = do_sha384 = true;
        }
    } else {
        // we know which version we use at this point
        if(sslVersionIsLikeTls12(ctx)) {
            do_sha1 = do_sha256 = do_sha384 = true;
        } else {
            do_md5 = do_sha1 = true;
        }
    }

    if (do_md5 &&
        (err = SSLHashMD5.update(&ctx->md5State, messageData)) != 0)
        goto done;
    if (do_sha1 &&
        (err = SSLHashSHA1.update(&ctx->shaState, messageData)) != 0)
        goto done;
    if (do_sha256 &&
        (err = SSLHashSHA256.update(&ctx->sha256State, messageData)) != 0)
        goto done;
    if (do_sha384 &&
        (err = SSLHashSHA384.update(&ctx->sha512State, messageData)) != 0)
        goto done;

    sslLogNegotiateDebug("%s protocol: %02X max: %02X cipher: %02X%s%s",
                         ctx->protocolSide == kSSLClientSide ? "client" : "server",
                         ctx->negProtocolVersion,
                         ctx->maxProtocolVersion,
                         ctx->selectedCipher,
                         do_md5 ? " md5" : "",
                         do_sha1 ? " sha1" : "",
                         do_sha256 ? " sha256" : "",
                         do_sha384 ? " sha384" : "");
done:
    return err;
}

OSStatus
SSLProcessHandshakeRecord(SSLRecord rec, SSLContext *ctx)
{   OSStatus        err;
    size_t          remaining;
    UInt8           *p;
	UInt8			*startingP;		// top of record we're parsing
    SSLHandshakeMsg message = {};
    SSLBuffer       messageData;

    if (ctx->fragmentedMessageCache.data != 0)
    {
		size_t origLen = ctx->fragmentedMessageCache.length;
		if ((err = SSLReallocBuffer(&ctx->fragmentedMessageCache,
                    ctx->fragmentedMessageCache.length + rec.contents.length,
                    ctx)) != 0)
        {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
            return err;
        }
        memcpy(ctx->fragmentedMessageCache.data + origLen,
            rec.contents.data, rec.contents.length);
        remaining = ctx->fragmentedMessageCache.length;
        p = ctx->fragmentedMessageCache.data;
    }
    else
    {   remaining = rec.contents.length;
        p = rec.contents.data;
    }
	startingP = p;

    size_t head = 4;

    while (remaining > 0)
    {
        if (remaining < head)
            break;  /* we must have at least a header */

        messageData.data = p;
        message.type = (SSLHandshakeType)*p++;
        message.contents.length = SSLDecodeSize(p, 3);


        p += 3;

        if ((message.contents.length + head) > remaining)
            break;

        message.contents.data = p;
        p += message.contents.length;
        messageData.length = head + message.contents.length;
        assert(p == messageData.data + messageData.length);

        /* message fragmentation */
        remaining -= messageData.length;
        if ((err = SSLProcessHandshakeMessage(message, ctx)) != 0)
            return err;

        if (message.type != SSL_HdskHelloRequest)

        {   if ((err = SSLUpdateHandshakeMacs(&messageData, ctx)) != 0)
            {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                return err;
            }
        }

        if ((err = SSLAdvanceHandshake(message.type, ctx)) != 0)
            return err;
    }

    if (remaining > 0)      /* Fragmented handshake message */
    {   /* If there isn't a cache, allocate one */
        if (ctx->fragmentedMessageCache.data == 0)
        {   if ((err = SSLAllocBuffer(&ctx->fragmentedMessageCache, remaining, ctx)) != 0)
            {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                return err;
            }
        }
        if (startingP != ctx->fragmentedMessageCache.data)
        {   memcpy(ctx->fragmentedMessageCache.data, startingP, remaining);
            ctx->fragmentedMessageCache.length = remaining;
        }
    }
    else if (ctx->fragmentedMessageCache.data != 0)
    {   if ((err = SSLFreeBuffer(&ctx->fragmentedMessageCache, ctx)) != 0)
        {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
            return err;
        }
    }

    return noErr;
}

OSStatus
DTLSProcessHandshakeRecord(SSLRecord rec, SSLContext *ctx)
{   OSStatus        err = errSecParam;
    size_t          remaining;
    UInt8           *p;
	UInt8			*startingP;		// top of record we're parsing

    const UInt32 head = 12;

    assert(ctx->isDTLS);

    remaining = rec.contents.length;
    p = rec.contents.data;
	startingP = p;

    while (remaining > 0)
    {
        UInt8 msgtype;
        UInt32 msglen;
        UInt32 msgseq;
        UInt32 fraglen;
        UInt32 fragofs;

        if (remaining < head) {
            /* flush it - record is too small */
            sslErrorLog("DTLSProcessHandshakeRecord: remaining too small (%lu out of %lu)\n", remaining, rec.contents.length);
            assert(0); // keep this assert until we find a test case that triggers it
            goto flushit;
        }

        /* Thats the 12 bytes of header : */
        msgtype = (SSLHandshakeType)*p++;
        msglen = SSLDecodeInt(p, 3); p+=3;
        msgseq = SSLDecodeInt(p, 2); p+=2;
        fragofs = SSLDecodeInt(p, 3); p+=3;
        fraglen = SSLDecodeInt(p, 3); p+=3;

        remaining -= head;

        SSLLogHdskMsg(msgtype, 0);
        sslHdskMsgDebug("DTLS Hdsk Record: type=%lu, len=%lu, seq=%lu (%lu), f_ofs=%lu, f_len=%lu, remaining=%lu",
                             msgtype, msglen, msgseq, ctx->hdskMessageSeqNext, fragofs, fraglen, remaining);

        if(
           ((fraglen+fragofs) > msglen)
           || (fraglen > remaining)
           || (msgseq!=ctx->hdskMessageSeqNext)
           || (fragofs!=ctx->hdskMessageCurrentOfs)
           || (fragofs && (msgtype!=ctx->hdskMessageCurrent.type))
           || (fragofs && (msglen != ctx->hdskMessageCurrent.contents.length))
           )
        {
            sslErrorLog("DTLSProcessHandshakeRecord: wrong fragment\n");
            // assert(0); // keep this assert until we find a test case that triggers it
            // This is a recoverable error, we just drop this fragment.
            // TODO: this should probably trigger a retransmit
            err = noErr;
            goto flushit;
        }

        /* First fragment - allocate */
        if(fragofs==0) {
            sslHdskMsgDebug("Allocating hdsk buf for msg type %d", msgtype);
            assert(ctx->hdskMessageCurrent.contents.data==NULL);
            assert(ctx->hdskMessageCurrent.contents.length==0);
            if((err=SSLAllocBuffer(&(ctx->hdskMessageCurrent.contents), msglen, ctx))!=0) {
                SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                return err;
            }
            ctx->hdskMessageCurrent.type = msgtype;
        }

        /* We have the next fragment, lets save it */
        memcpy(ctx->hdskMessageCurrent.contents.data + ctx->hdskMessageCurrentOfs, p, fraglen);
        ctx->hdskMessageCurrentOfs+=fraglen;
        p+=fraglen;
        remaining-=fraglen;

        /* This was the last fragment, lets process the message */
        if(ctx->hdskMessageCurrentOfs == ctx->hdskMessageCurrent.contents.length) {
            err = SSLProcessHandshakeMessage(ctx->hdskMessageCurrent, ctx);
            if(err)
                goto flushit;

            if ((msgtype != SSL_HdskHelloRequest) && (msgtype != SSL_HdskHelloVerifyRequest))
            {
                /* We need to hash a fake header as if no fragmentation */
                uint8_t pseudo_header[head];
                SSLBuffer header;
                header.data=pseudo_header;
                header.length=head;

                pseudo_header[0]=msgtype;
                SSLEncodeInt(pseudo_header+1, msglen, 3);
                SSLEncodeInt(pseudo_header+4, msgseq, 2);
                SSLEncodeInt(pseudo_header+6, 0, 3);
                SSLEncodeInt(pseudo_header+9, msglen, 3);

                if ((err = SSLHashSHA1.update(&ctx->shaState, &header)) != 0 ||
                    (err = SSLHashMD5.update(&ctx->md5State, &header)) != 0)
                {
                    SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                    goto flushit;
                }

                SSLBuffer *messageData=&ctx->hdskMessageCurrent.contents;
                if ((err = SSLHashSHA1.update(&ctx->shaState, messageData)) != 0 ||
                    (err = SSLHashMD5.update(&ctx->md5State, messageData)) != 0)
                {
                    SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                    goto flushit;
                }

                sslHdskMsgDebug("Hashing %d bytes of msg seq %ld\n", messageData->length, msgseq);
            }

            sslHdskMsgDebug("processed message of type %d", msgtype);

            if ((err = SSLAdvanceHandshake(msgtype, ctx)) != 0)
            {
                sslErrorLog("AdvanceHandshake error: %"PRIstatus"\n", err);
                goto flushit;
            }

            /* Free the buffer for current message, and reset offset */
            SSLFreeBuffer(&(ctx->hdskMessageCurrent.contents), ctx);
            ctx->hdskMessageCurrentOfs=0;

            /* If we successfully processed a message, we wait for the next one */
            ctx->hdskMessageSeqNext++;

        }

        sslHdskMsgDebug("remaining = %ld", remaining);
    }

    return noErr;

flushit:
    sslErrorLog("DTLSProcessHandshakeRecord: flusing record (err=%"PRIstatus")\n", err);

    /* This will flush the current handshake message */
    SSLFreeBuffer(&(ctx->hdskMessageCurrent.contents), ctx);
    ctx->hdskMessageCurrentOfs=0;

    return err;
}

OSStatus
DTLSRetransmit(SSLContext *ctx)
{
    sslHdskMsgDebug("DTLSRetransmit in state %s. Last Sent = %d, Last Recv=%d, timeout=%f\n",
                hdskStateToStr(ctx->state), ctx->hdskMessageSeq, ctx->hdskMessageSeqNext, ctx->timeout_duration);

    /* Too many retransmits, just give up!! */
    if(ctx->hdskMessageRetryCount>10)
        return errSSLConnectionRefused;

    /* go back to previous cipher if retransmitting a flight including changecipherspec */
    if(ctx->messageQueueContainsChangeCipherSpec) {
        ctx->writePending = ctx->writeCipher;
        ctx->writeCipher = ctx->prevCipher;
    }

    /* set timeout deadline */
    ctx->hdskMessageRetryCount++;
    ctx->timeout_deadline = CFAbsoluteTimeGetCurrent()+((1<<ctx->hdskMessageRetryCount)*ctx->timeout_duration);

    /* Lets resend the last flight */
    return SSLSendFlight(ctx);
}

static OSStatus
SSLProcessHandshakeMessage(SSLHandshakeMsg message, SSLContext *ctx)
{   OSStatus      err;

    err = noErr;
    SSLLogHdskMsg(message.type, 0);
    switch (message.type)
    {   case SSL_HdskHelloRequest:
            if (ctx->protocolSide != kSSLClientSide)
                goto wrongMessage;
            if (message.contents.length > 0)
                err = errSSLProtocol;
            break;
        case SSL_HdskClientHello:
            if (ctx->state != SSL_HdskStateServerUninit)
                goto wrongMessage;
            err = SSLProcessClientHello(message.contents, ctx);
            break;
        case SSL_HdskServerHello:
            if (ctx->state != SSL_HdskStateServerHello &&
                ctx->state != SSL_HdskStateServerHelloUnknownVersion)
                goto wrongMessage;
            err = SSLProcessServerHello(message.contents, ctx);
            break;
#if ENABLE_DTLS
        case SSL_HdskHelloVerifyRequest:
            if (ctx->protocolSide != kSSLClientSide)
                goto wrongMessage;
            if(ctx->state != SSL_HdskStateServerHello)
                goto wrongMessage;
            /* TODO: Do we need to check the client state here ? */
            err = SSLProcessServerHelloVerifyRequest(message.contents, ctx);
            break;
#endif
        case SSL_HdskCert:
            if (ctx->state != SSL_HdskStateCert &&
                ctx->state != SSL_HdskStateClientCert)
                goto wrongMessage;
			err = SSLProcessCertificate(message.contents, ctx);
			/*
			 * Note that cert evaluation can now be performed asynchronously,
			 * so SSLProcessCertificate may return errSSLWouldBlock here.
			 */
            break;
        case SSL_HdskCertRequest:
            if (((ctx->state != SSL_HdskStateHelloDone) &&
			     (ctx->state != SSL_HdskStateKeyExchange))
                 || ctx->certRequested)
                goto wrongMessage;
            err = SSLProcessCertificateRequest(message.contents, ctx);
            if (ctx->breakOnCertRequest)
                ctx->signalCertRequest = true;
            break;
        case SSL_HdskServerKeyExchange:
       		/*
		 * Since this message is optional for some key exchange
			 * mechanisms, and completely at the server's discretion,
			 * we need to be able to handle this in one of two states...
        	 */
        	switch(ctx->state) {
        		case SSL_HdskStateKeyExchange:	/* explicitly waiting for this */
        		case SSL_HdskStateHelloDone:
        			break;
        		default:
                	goto wrongMessage;
        	}
            err = SSLProcessServerKeyExchange(message.contents, ctx);
            break;
        case SSL_HdskServerHelloDone:
            if (ctx->state != SSL_HdskStateHelloDone)
                goto wrongMessage;
            err = SSLProcessServerHelloDone(message.contents, ctx);
            break;
        case SSL_HdskCertVerify:
            if (ctx->state != SSL_HdskStateClientCertVerify)
                goto wrongMessage;
            err = SSLProcessCertificateVerify(message.contents, ctx);
			assert(ctx->protocolSide == kSSLServerSide);
			if(err) {
				ctx->clientCertState = kSSLClientCertRejected;
			}
            break;
        case SSL_HdskClientKeyExchange:
            if (ctx->state != SSL_HdskStateClientKeyExchange)
                goto wrongMessage;
            err = SSLProcessKeyExchange(message.contents, ctx);
            break;
        case SSL_HdskFinished:
            if (ctx->state != SSL_HdskStateFinished)
                goto wrongMessage;
            err = SSLProcessFinished(message.contents, ctx);
            break;
        default:
            goto wrongMessage;
            break;
    }

    if (err && !ctx->sentFatalAlert)
    {   if (err == errSSLProtocol)
            SSLFatalSessionAlert(SSL_AlertIllegalParam, ctx);
        else if (err == errSSLNegotiation)
            SSLFatalSessionAlert(SSL_AlertHandshakeFail, ctx);
        else if (err != errSSLWouldBlock &&
				 err != errSSLServerAuthCompleted /* == errSSLClientAuthCompleted */ &&
				 err != errSSLClientCertRequested)
            SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
    }
    return err;

wrongMessage:
    SSLFatalSessionAlert(SSL_AlertUnexpectedMsg, ctx);
    return errSSLProtocol;
}

/*
 * Given a server-side SSLContext that's fully restored for a resumed session,
 * queue up the remaining outgoing messages to finish the handshake.
 */
static OSStatus
SSLResumeServerSide(
	SSLContext *ctx)
{
	OSStatus err;
	if ((err = SSLPrepareAndQueueMessage(SSLEncodeServerHello, ctx)) != 0)
		return err;
	if ((err = SSLInitPendingCiphers(ctx)) != 0)
	{   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
		return err;
	}
	if ((err = SSLPrepareAndQueueMessage(SSLEncodeChangeCipherSpec,
				ctx)) != 0)
		return err;

	if ((err = SSLPrepareAndQueueMessage(SSLEncodeFinishedMessage,
			ctx)) != 0)
		return err;

	SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec);

	return noErr;

}

OSStatus
SSLAdvanceHandshake(SSLHandshakeType processed, SSLContext *ctx)
{   OSStatus        err;
    SSLBuffer       sessionIdentifier;

    SSLResetFlight(ctx);

    switch (processed)
    {
#if ENABLE_DTLS
        case SSL_HdskHelloVerifyRequest:
            /* Just fall through */
#endif
        case SSL_HdskHelloRequest:
			/*
			 * Reset the client auth state machine in case this is
			 * a renegotiation.
			 */
			ctx->certRequested = 0;
			ctx->certSent = 0;
			ctx->certReceived = 0;
			ctx->x509Requested = 0;
			ctx->clientCertState = kSSLClientCertNone;
			ctx->readCipher.ready = 0;
			ctx->writeCipher.ready = 0;
			ctx->wroteAppData = 0;
            if ((err = SSLPrepareAndQueueMessage(SSLEncodeClientHello, ctx)) != 0)
                return err;
            SSLChangeHdskState(ctx, SSL_HdskStateServerHello);
            break;
        case SSL_HdskClientHello:
            assert(ctx->protocolSide == kSSLServerSide);
			ctx->sessionMatch = 0;

            if((ctx->negProtocolVersion==DTLS_Version_1_0) && (ctx->cookieVerified==false))
            {   /* Send Hello Verify Request */
                if((err=SSLPrepareAndQueueMessage(SSLEncodeServerHelloVerifyRequest, ctx)) !=0 )
                    return err;
                break;
            }

			#if 	SSL_PAC_SERVER_ENABLE
			if((ctx->sessionTicket.data != NULL) &&
			   (ctx->masterSecretCallback != NULL)) {
				/*
				 * Client sent us a session ticket and we know how to ask
				 * the app for master secret. Go for it.
				 */
				size_t secretLen = SSL_MASTER_SECRET_SIZE;
				sslEapDebug("Server side resuming based on masterSecretCallback");

				/* the master secret callback requires serverRandom, now... */
			    if ((err = SSLEncodeRandom(ctx->serverRandom, ctx)) != 0)
       				 return err;
				ctx->serverRandomValid = 1;

				ctx->masterSecretCallback(ctx, ctx->masterSecretArg,
					ctx->masterSecret, &secretLen);
				ctx->sessionMatch = 1;
				/* set up selectedCipherSpec */
				if ((err = FindCipherSpec(ctx)) != 0) {
					return err;
				}
				/* queue up remaining messages to finish handshake */
				if((err = SSLResumeServerSide(ctx)) != 0)
					return err;
				break;
			}
			#endif	/* SSL_PAC_SERVER_ENABLE */
            if (ctx->sessionID.data != 0)
			/* If session ID != 0, client is trying to resume */
            {   if (ctx->resumableSession.data != 0)
                {
					SSLProtocolVersion sessionProt;
					if ((err = SSLRetrieveSessionID(ctx->resumableSession,
								&sessionIdentifier, ctx)) != 0)
                        return err;
					if ((err = SSLRetrieveSessionProtocolVersion(ctx->resumableSession,
							&sessionProt, ctx)) != 0)
					{   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
						return err;
					}
					if ((sessionIdentifier.length == ctx->sessionID.length) &&
                        (memcmp(sessionIdentifier.data, ctx->sessionID.data,
							ctx->sessionID.length) == 0) &&
					    (sessionProt == ctx->negProtocolVersion))
                    {   /* Everything matches; resume the session */
						sslLogResumSessDebug("===RESUMING SSL3 server-side session");
                        if ((err = SSLInstallSessionFromData(ctx->resumableSession,
								ctx)) != 0)
                        {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                            return err;
                        }
						ctx->sessionMatch = 1;
						SSLFreeBuffer(&sessionIdentifier, ctx);

						/* queue up remaining messages to finish handshake */
						if((err = SSLResumeServerSide(ctx)) != 0)
							return err;
                        break;
                    }
					else {
						sslLogResumSessDebug(
							"===FAILED TO RESUME SSL3 server-side session");
					}
                    if ((err = SSLFreeBuffer(&sessionIdentifier, ctx)) != 0 ||
                        (err = SSLDeleteSessionData(ctx)) != 0)
                    {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                        return err;
                    }
                }
                if ((err = SSLFreeBuffer(&ctx->sessionID, ctx)) != 0)
                {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                    return err;
                }
            }

            /*
			 * If we get here, we're not resuming; generate a new session ID
			 * if we know our peer
			 */
            if (ctx->peerID.data != 0)
            {   /* Ignore errors; just treat as uncached session */
                assert(ctx->sessionID.data == 0);
                err = SSLAllocBuffer(&ctx->sessionID, SSL_SESSION_ID_LEN, ctx);
                if (err == 0)
                {
                	if((err = sslRand(ctx, &ctx->sessionID)) != 0)
                    {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                        return err;
                    }
                }
            }

            if ((err = SSLPrepareAndQueueMessage(SSLEncodeServerHello, ctx)) != 0)
                return err;
            switch (ctx->selectedCipherSpec.keyExchangeMethod)
            {   case SSL_NULL_auth:
            	#if		APPLE_DH
                case SSL_DH_anon:
                case SSL_DH_anon_EXPORT:
                	if(ctx->clientAuth == kAlwaysAuthenticate) {
                		/* app requires this; abort */
                		SSLFatalSessionAlert(SSL_AlertHandshakeFail, ctx);
                		return errSSLNegotiation;
                	}
                	ctx->tryClientAuth = false;
					/* DH server side needs work */
                    break;
                #endif	/* APPLE_DH */
                default:        /* everything else */
					if(ctx->localCert == NULL) {
						/* no cert but configured for, and negotiated, a
						 * ciphersuite which requires one */
						sslErrorLog("SSLAdvanceHandshake: No server key!\n");
						return errSSLBadConfiguration;
					}
                    if ((err = SSLPrepareAndQueueMessage(SSLEncodeCertificate,
							ctx)) != 0)
                        return err;
                    break;
            }
			/*
			 * At this point we decide whether to send a server key exchange
			 * method. For Apple servers, I think we'll ALWAYS do this, because
			 * of key usage restrictions (can't decrypt and sign with the same
			 * private key), but conceptually in this code, we do it if
			 * enabled by the presence of encryptPrivKey.
			 */
			{
				bool doServerKeyExch = false;
				switch(ctx->selectedCipherSpec.keyExchangeMethod) {
					case SSL_RSA_EXPORT:
					#if !SSL_SERVER_KEYEXCH_HACK
					/* the "proper" way - app decides. */
					case SSL_RSA:
					#endif
						if(ctx->encryptPrivKeyRef != NULL) {
							doServerKeyExch = true;
						}
						break;
					case SSL_DH_anon:
					case SSL_DH_anon_EXPORT:
					case SSL_DHE_RSA:
					case SSL_DHE_RSA_EXPORT:
					case SSL_DHE_DSS:
					case SSL_DHE_DSS_EXPORT:
						doServerKeyExch = true;
						break;
					default:
						break;
				}
				if(doServerKeyExch) {
					err = SSLPrepareAndQueueMessage(SSLEncodeServerKeyExchange, ctx);
					if(err) {
						return err;
					}
				}
			}
            if (ctx->tryClientAuth)
            {   if ((err = SSLPrepareAndQueueMessage(SSLEncodeCertificateRequest,
						ctx)) != 0)
                    return err;
                ctx->certRequested = 1;
				ctx->clientCertState = kSSLClientCertRequested;
            }
            if ((err = SSLPrepareAndQueueMessage(SSLEncodeServerHelloDone, ctx)) != 0)
                return err;
            if (ctx->certRequested) {
                SSLChangeHdskState(ctx, SSL_HdskStateClientCert);
            }
            else {
                SSLChangeHdskState(ctx, SSL_HdskStateClientKeyExchange);
            }
            break;
        case SSL_HdskServerHello:
			ctx->sessionMatch = 0;
            if (ctx->resumableSession.data != 0 && ctx->sessionID.data != 0)
            {
				SSLProtocolVersion sessionProt;
				if ((err = SSLRetrieveSessionID(ctx->resumableSession,
						&sessionIdentifier, ctx)) != 0)
                {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                    return err;
                }
				if ((err = SSLRetrieveSessionProtocolVersion(ctx->resumableSession,
						&sessionProt, ctx)) != 0)
                {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                    return err;
                }
                if ((sessionIdentifier.length == ctx->sessionID.length) &&
                    (memcmp(sessionIdentifier.data, ctx->sessionID.data,
							ctx->sessionID.length) == 0) &&
					(sessionProt == ctx->negProtocolVersion))
                {   /* Everything matches; resume the session */
					sslLogResumSessDebug("===RESUMING SSL3 client-side session");
                    if ((err = SSLInstallSessionFromData(ctx->resumableSession,
							ctx)) != 0 ||
                        (err = SSLInitPendingCiphers(ctx)) != 0 ||
                        (err = SSLFreeBuffer(&sessionIdentifier, ctx)) != 0)
                    {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                        return err;
                    }
					ctx->sessionMatch = 1;
                    SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec);
                    break;
                }
				else {
					sslLogResumSessDebug("===FAILED TO RESUME SSL3 client-side "
							"session");
				}
                if ((err = SSLFreeBuffer(&sessionIdentifier, ctx)) != 0)
                {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                    return err;
                }
            }
            switch (ctx->selectedCipherSpec.keyExchangeMethod)
            {
            	/* these require a key exchange message */
            	case SSL_NULL_auth:
                case SSL_DH_anon:
                case SSL_DH_anon_EXPORT:
                    SSLChangeHdskState(ctx, SSL_HdskStateKeyExchange);
                    break;
                case SSL_RSA:
                case SSL_DH_DSS:
                case SSL_DH_DSS_EXPORT:
                case SSL_DH_RSA:
                case SSL_DH_RSA_EXPORT:
                case SSL_RSA_EXPORT:
                case SSL_DHE_DSS:
                case SSL_DHE_DSS_EXPORT:
                case SSL_DHE_RSA:
                case SSL_DHE_RSA_EXPORT:
                case SSL_Fortezza:
				case SSL_ECDH_ECDSA:
				case SSL_ECDHE_ECDSA:
				case SSL_ECDH_RSA:
				case SSL_ECDHE_RSA:
                    SSLChangeHdskState(ctx, SSL_HdskStateCert);
                    break;
                default:
                    assert("Unknown key exchange method");
                    break;
            }
            break;
        case SSL_HdskCert:
            if (ctx->state == SSL_HdskStateCert)
                switch (ctx->selectedCipherSpec.keyExchangeMethod)
                {   case SSL_RSA:
                 	/*
                	 * I really think the two RSA cases should be
                	 * handled the same here - the server key exchange is
                	 * optional, and is up to the server.
                	 * Note this isn't the same as SSL_SERVER_KEYEXCH_HACK;
                	 * we're a client here.
                	 */
                	case SSL_RSA_EXPORT:
                    case SSL_DH_DSS:
                    case SSL_DH_DSS_EXPORT:
                    case SSL_DH_RSA:
                    case SSL_DH_RSA_EXPORT:
					case SSL_ECDH_ECDSA:
					case SSL_ECDH_RSA:
                        SSLChangeHdskState(ctx, SSL_HdskStateHelloDone);
                        break;
                    case SSL_DHE_DSS:
                    case SSL_DHE_DSS_EXPORT:
                    case SSL_DHE_RSA:
                    case SSL_DHE_RSA_EXPORT:
                    case SSL_Fortezza:
					case SSL_ECDHE_ECDSA:
					case SSL_ECDHE_RSA:
                        SSLChangeHdskState(ctx, SSL_HdskStateKeyExchange);
                        break;
                    default:
                        assert("Unknown or unexpected key exchange method");
                        break;
                }
            else if (ctx->state == SSL_HdskStateClientCert)
            {   SSLChangeHdskState(ctx, SSL_HdskStateClientKeyExchange);
                if (ctx->peerCert != 0)
                    ctx->certReceived = 1;
            }
            break;
        case SSL_HdskCertRequest:
			/* state stays in SSL_HdskStateHelloDone; distinction is in
			 *  ctx->certRequested */
            if (ctx->peerCert == 0)
            {   SSLFatalSessionAlert(SSL_AlertHandshakeFail, ctx);
                return errSSLProtocol;
            }
            assert(ctx->protocolSide == kSSLClientSide);
            ctx->certRequested = 1;
            ctx->clientCertState = kSSLClientCertRequested;
            break;
        case SSL_HdskServerKeyExchange:
            SSLChangeHdskState(ctx, SSL_HdskStateHelloDone);
            break;
        case SSL_HdskServerHelloDone:
			/*
             * Waiting until server has sent hello done to interrupt and allow
             * setting client cert, so we can send certificate, keyexchange and
             * cert verify message together
			 */
            if (ctx->state != SSL_HdskStateClientCert) {
                if (ctx->signalServerAuth) {
                    ctx->signalServerAuth = false;
                    SSLChangeHdskState(ctx, SSL_HdskStateClientCert);
                    return errSSLServerAuthCompleted;
                } else if (ctx->signalCertRequest) {
                    ctx->signalCertRequest = false;
                    SSLChangeHdskState(ctx, SSL_HdskStateClientCert);
                    return errSSLClientCertRequested;
                } else if (ctx->signalClientAuth) {
                    ctx->signalClientAuth = false;
                    return errSSLClientAuthCompleted;
                }
            }

			if (ctx->clientCertState == kSSLClientCertRequested) {
				/*
				 * Server wants a client authentication cert - do
				 * we have one?
				 */
                if (ctx->localCert != 0 && ctx->x509Requested) {
					if ((err = SSLPrepareAndQueueMessage(SSLEncodeCertificate,
							ctx)) != 0) {
						return err;
					}
                }
                else {
					/* response for no cert depends on protocol version */
					if(ctx->negProtocolVersion >= TLS_Version_1_0) {
						/* TLS: send empty cert msg */
						if ((err = SSLPrepareAndQueueMessage(SSLEncodeCertificate,
								ctx)) != 0) {
							return err;
						}
					}
					else {
						/* SSL3: "no cert" alert */
						if ((err = SSLSendAlert(SSL_AlertLevelWarning, SSL_AlertNoCert_RESERVED,
								ctx)) != 0) {
							return err;
						}
					}
                }	/* no cert to send */
            }	/* server requested a cert */
            if ((err = SSLPrepareAndQueueMessage(SSLEncodeKeyExchange, ctx)) != 0)
                return err;
			assert(ctx->sslTslCalls != NULL);
            if ((err = ctx->sslTslCalls->generateMasterSecret(ctx)) != 0 ||
                (err = SSLInitPendingCiphers(ctx)) != 0)
            {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                return err;
            }
			memset(ctx->preMasterSecret.data, 0, ctx->preMasterSecret.length);
            if ((err = SSLFreeBuffer(&ctx->preMasterSecret, ctx)) != 0) {
                return err;
			}
            if (ctx->certSent) {
				/* Not all client auth mechanisms require a cert verify message */
				switch(ctx->negAuthType) {
					case SSLClientAuth_RSASign:
					case SSLClientAuth_ECDSASign:
						if ((err = SSLPrepareAndQueueMessage(SSLEncodeCertificateVerify,
								ctx)) != 0) {
							return err;
						}
						break;
					default:
						break;
				}
			}
            if ((err = SSLPrepareAndQueueMessage(SSLEncodeChangeCipherSpec,
					ctx)) != 0) {
                return err;
			}
            if ((err = SSLPrepareAndQueueMessage(SSLEncodeFinishedMessage, ctx)) != 0)
                return err;
            SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec);
            break;
        case SSL_HdskCertVerify:
            SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec);
            break;
        case SSL_HdskClientKeyExchange:
 			assert(ctx->sslTslCalls != NULL);
			if ((err = ctx->sslTslCalls->generateMasterSecret(ctx)) != 0 ||
                (err = SSLInitPendingCiphers(ctx)) != 0)
            {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                return err;
            }
			memset(ctx->preMasterSecret.data, 0, ctx->preMasterSecret.length);
            if ((err = SSLFreeBuffer(&ctx->preMasterSecret, ctx)) != 0)
                return err;
            if (ctx->certReceived) {
                SSLChangeHdskState(ctx, SSL_HdskStateClientCertVerify);
            }
            else {
                SSLChangeHdskState(ctx, SSL_HdskStateChangeCipherSpec);
            }
            break;
        case SSL_HdskFinished:
            /* Handshake is over; enable data transfer on read channel */
            ctx->readCipher.ready = 1;
            /* If writePending is set, we haven't yet sent a finished message;
			 * send it */
            if (ctx->writePending.ready != 0)
            {   if ((err = SSLPrepareAndQueueMessage(SSLEncodeChangeCipherSpec,
						ctx)) != 0)
                    return err;
                if ((err = SSLPrepareAndQueueMessage(SSLEncodeFinishedMessage,
							ctx)) != 0)
                    return err;
            }
            if (ctx->protocolSide == kSSLServerSide) {
                SSLChangeHdskState(ctx, SSL_HdskStateServerReady);
            }
            else {
                SSLChangeHdskState(ctx, SSL_HdskStateClientReady);
            }
            if ((ctx->peerID.data != 0) && (ctx->sessionTicket.data == NULL)) {
				/* note we avoid caching session data for PAC-style resumption */
                SSLAddSessionData(ctx);
			}
            break;
        default:
            assert(0);
            break;
    }

    /* We should have a full flight when we reach here, sending it for the first time */
    ctx->hdskMessageRetryCount = 0;
    ctx->timeout_deadline = CFAbsoluteTimeGetCurrent() + ctx->timeout_duration;
    return SSLSendFlight(ctx);
}

OSStatus
SSLPrepareAndQueueMessage(EncodeMessageFunc msgFunc, SSLContext *ctx)
{   OSStatus        err;
    SSLRecord       rec = {0, 0, {0, NULL}};
    WaitingMessage  *out;
    WaitingMessage  *queue;

    if ((err = msgFunc(&rec, ctx)) != 0)
    {   SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
        goto fail;
    }

    if (rec.contentType == SSL_RecordTypeHandshake)
    {
        if ((err = SSLUpdateHandshakeMacs(&rec.contents, ctx)) != 0)
        {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
            goto fail;
        }
        SSLLogHdskMsg((SSLHandshakeType)rec.contents.data[0], 1);
        ctx->hdskMessageSeq++;
    }

    err=errSSLInternal;
    out = (WaitingMessage *)sslMalloc(sizeof(WaitingMessage));
    if(out==NULL) goto fail;

    out->next = NULL;
	out->rec = rec;

    queue=ctx->messageWriteQueue;
    if (queue == NULL) {
        sslHdskMsgDebug("Queuing first message in flight\n");
        ctx->messageWriteQueue = out;
    } else {
        int n=1;
        while (queue->next != 0) {
            queue = queue->next;
            n++;
        }
        sslHdskMsgDebug("Queuing message %d in flight\n", n);
        queue->next = out;
    }

    return noErr;
fail:
    SSLFreeBuffer(&rec.contents, ctx);
    return err;
}

static
OSStatus SSLSendMessage(SSLRecord rec, SSLContext *ctx)
{
    OSStatus err;

    assert(ctx->sslTslCalls != NULL);

    if ((err = ctx->sslTslCalls->writeRecord(rec, ctx)) != 0)
        return err;
    if(rec.contentType == SSL_RecordTypeChangeCipher) {
        /* Install new cipher spec on write side */
        if ((err = SSLDisposeCipherSuite(&ctx->writeCipher, ctx)) != 0)
        {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
            return err;
        }
        ctx->prevCipher = ctx->writeCipher;
        ctx->writeCipher = ctx->writePending;
        /* Can't send data until Finished is sent */
        ctx->writeCipher.ready = 0;
		ctx->wroteAppData = 0;

        /* Zero out old data */
        memset(&ctx->writePending, 0, sizeof(CipherContext));
        ctx->writePending.encrypting = 1;

        /* TODO: that should only happen after Finished message is sent. <rdar://problem/9682471> */
        ctx->writeCipher.ready = 1;
    }

    return noErr;
}

static
OSStatus DTLSSendMessage(SSLRecord rec, SSLContext *ctx)
{
    OSStatus err=noErr;

    assert(ctx->sslTslCalls != NULL);

    if(rec.contentType != SSL_RecordTypeHandshake) {
        sslHdskMsgDebug("Not fragmenting message type=%d len=%d\n", rec.contentType, rec.contents.length);
        if ((err = ctx->sslTslCalls->writeRecord(rec, ctx)) != 0)
            return err;
        if(rec.contentType == SSL_RecordTypeChangeCipher) {
            /* Install new cipher spec on write side */
            if ((err = SSLDisposeCipherSuite(&ctx->writeCipher, ctx)) != 0)
            {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
                return err;
            }
            ctx->prevCipher = ctx->writeCipher;
            ctx->writeCipher = ctx->writePending;
			/* Can't send data until Finished is sent */
            ctx->writeCipher.ready = 0;
			ctx->wroteAppData = 0;

			/* Zero out old data */
            memset(&ctx->writePending, 0, sizeof(CipherContext));
			ctx->writePending.encrypting = 1;

            /* TODO: that should only happen after Finished message is sent. See <rdar://problem/9682471> */
            ctx->writeCipher.ready = 1;

        }
    } else {
    /* fragmenting */
        SSLRecord fragrec;

        int msghead = 12;  /* size of message header in DTLS */
        size_t fraglen;
        size_t len = rec.contents.length-msghead;
        UInt32 seq = SSLDecodeInt(rec.contents.data+4, 2);
        (void) seq; // Suppress warnings
        size_t ofs = 0;

        sslHdskMsgDebug("Fragmenting msg seq %ld (rl=%d, ml=%d)", seq, rec.contents.length,
                        SSLDecodeInt(rec.contents.data+1, 3));


        SSLGetDatagramWriteSize(ctx, &fraglen);
        fraglen -=  msghead;

        fragrec.contentType = rec.contentType;
        fragrec.protocolVersion = rec.protocolVersion;
        if((err=SSLAllocBuffer(&fragrec.contents, fraglen + msghead, ctx))!=0)
            return err;

        /* copy the constant part of the header */
        memcpy(fragrec.contents.data,rec.contents.data, 6);

        while(len>fraglen) {

            sslHdskMsgDebug("Fragmenting msg seq %ld (o=%d,l=%d)", seq, ofs, fraglen);

            /* fragment offset and fragment length */
            SSLEncodeSize(fragrec.contents.data+6, ofs, 3);
            SSLEncodeSize(fragrec.contents.data+9, fraglen, 3);
            /* copy the payload */
            memcpy(fragrec.contents.data+msghead, rec.contents.data+msghead+ofs, fraglen);
            if ((err = ctx->sslTslCalls->writeRecord(fragrec, ctx)) != 0)
                goto cleanup;
            len-=fraglen;
            ofs+=fraglen;
        }

        sslHdskMsgDebug("Fragmenting msg seq %ld - Last Fragment (o=%d,l=%d)", seq, ofs, len);

        /* last fragment */
        /* fragment offset and fragment length */
        SSLEncodeSize(fragrec.contents.data+6, ofs, 3);
        SSLEncodeSize(fragrec.contents.data+9, len, 3);
        /* copy the payload */
        memcpy(fragrec.contents.data+msghead, rec.contents.data+msghead+ofs, len);
        fragrec.contents.length=len+msghead;
        err = ctx->sslTslCalls->writeRecord(fragrec, ctx);

    cleanup:
        /* Free the allocated fragment buffer */
        SSLFreeBuffer(&fragrec.contents, ctx);

    }

    return err;
}


OSStatus SSLResetFlight(SSLContext *ctx)
{
    OSStatus err;
    WaitingMessage *queue;
    WaitingMessage *next;
    int n=0;

    assert(ctx->sslTslCalls != NULL);

    queue=ctx->messageWriteQueue;
    ctx->messageQueueContainsChangeCipherSpec=false;

    while(queue) {
        n++;
        err = SSLFreeBuffer(&queue->rec.contents, ctx);
        if (err != 0)
            goto fail;
        next=queue->next;
        sslFree(queue);
        queue=next;
    }

    ctx->messageWriteQueue=NULL;

    return noErr;
fail:
    assert(0);
    return err;
}


OSStatus SSLSendFlight(SSLContext *ctx)
{
    OSStatus err;
    WaitingMessage  *queue;
    int n=0;

    assert(ctx->sslTslCalls != NULL);

    queue=ctx->messageWriteQueue;

    while(queue) {
        if (ctx->isDTLS) {
            err=DTLSSendMessage(queue->rec, ctx);
        } else {
            err=SSLSendMessage(queue->rec, ctx);
        }
        if (err != 0)
            goto fail;
        queue=queue->next;
        n++;
    }

    return noErr;
fail:
    assert(0);
    return err;
}

OSStatus
SSL3ReceiveSSL2ClientHello(SSLRecord rec, SSLContext *ctx)
{   OSStatus      err;

    if ((err = SSLInitMessageHashes(ctx)) != 0)
        return err;

    if ((err = SSLHashSHA1.update(&ctx->shaState, &rec.contents)) != 0 ||
        (err = SSLHashMD5.update(&ctx->md5State, &rec.contents)) != 0)
    {   SSLFatalSessionAlert(SSL_AlertInternalError, ctx);
        return err;
    }

    if ((err = SSLAdvanceHandshake(SSL_HdskClientHello, ctx)) != 0)
        return err;

    return noErr;
}

/* log changes in handshake state */
#ifndef	NDEBUG
#include <stdio.h>

char *hdskStateToStr(SSLHandshakeState state)
{
	static char badStr[100];

	switch(state) {
		case SSL_HdskStateUninit:
			return "Uninit";
		case SSL_HdskStateServerUninit:
			return "ServerUninit";
		case SSL_HdskStateClientUninit:
			return "ClientUninit";
		case SSL_HdskStateGracefulClose:
			return "GracefulClose";
		case SSL_HdskStateErrorClose:
			return "ErrorClose";
		case SSL_HdskStateNoNotifyClose:
			return "NoNotifyClose";
		case SSL_HdskStateServerHello:
			return "ServerHello";
		case SSL_HdskStateServerHelloUnknownVersion:
			return "ServerHelloUnknownVersion";
		case SSL_HdskStateKeyExchange:
			return "KeyExchange";
		case SSL_HdskStateCert:
			return "Cert";
		case SSL_HdskStateHelloDone:
			return "HelloDone";
		case SSL_HdskStateClientCert:
			return "ClientCert";
		case SSL_HdskStateClientKeyExchange:
			return "ClientKeyExchange";
		case SSL_HdskStateClientCertVerify:
			return "ClientCertVerify";
		case SSL_HdskStateChangeCipherSpec:
			return "ChangeCipherSpec";
		case SSL_HdskStateFinished:
			return "Finished";
		case SSL2_HdskStateClientMasterKey:
			return "SSL2_ClientMasterKey";
		case SSL2_HdskStateClientFinished:
			return "SSL2_ClientFinished";
		case SSL2_HdskStateServerHello:
			return "SSL2_ServerHello";
		case SSL2_HdskStateServerVerify:
			return "SSL2_ServerVerify";
		case SSL2_HdskStateServerFinished:
			return "SSL2_ServerFinished";
		case SSL_HdskStateServerReady:
			return "SSL_ServerReady";
		case SSL_HdskStateClientReady:
			return "SSL_ClientReady";
		default:
			sprintf(badStr, "Unknown state (%d(d)", state);
			return badStr;
	}
}

void SSLChangeHdskState(SSLContext *ctx, SSLHandshakeState newState)
{
	/* FIXME - this ifndef should not be necessary */
	#ifndef	NDEBUG
	sslHdskStateDebug("...hdskState = %s", hdskStateToStr(newState));
	#endif
	ctx->state = newState;
}


/* log handshake messages */

static char *hdskMsgToStr(SSLHandshakeType msg)
{
	static char badStr[100];

	switch(msg) {
		case SSL_HdskHelloRequest:
			return "SSL_HdskHelloRequest";
		case SSL_HdskClientHello:
			return "SSL_HdskClientHello";
		case SSL_HdskServerHello:
			return "SSL_HdskServerHello";
        case SSL_HdskHelloVerifyRequest:
			return "SSL_HdskHelloVerifyRequest";
		case SSL_HdskCert:
			return "SSL_HdskCert";
		case SSL_HdskServerKeyExchange:
			return "SSL_HdskServerKeyExchange";
		case SSL_HdskCertRequest:
			return "SSL_HdskCertRequest";
		case SSL_HdskServerHelloDone:
			return "SSL_HdskServerHelloDone";
		case SSL_HdskCertVerify:
			return "SSL_HdskCertVerify";
		case SSL_HdskClientKeyExchange:
			return "SSL_HdskClientKeyExchange";
		case SSL_HdskFinished:
			return "SSL_HdskFinished";
		default:
			sprintf(badStr, "Unknown msg (%d(d))", msg);
			return badStr;
	}
}

void SSLLogHdskMsg(SSLHandshakeType msg, char sent)
{
	sslHdskMsgDebug("---%s handshake msg %s",
		hdskMsgToStr(msg), (sent ? "sent" : "recv"));
}

#endif	/* NDEBUG */