//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995.
//
//  File:       ssl2srv.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    8-08-95   RichardW   Created
//
//----------------------------------------------------------------------------

#include <spbase.h>
#include <ssl2msg.h>
#include <ssl2prot.h>



SP_STATUS Ssl2SrvGenerateServerFinish(PSPContext pContext,
                              PSPBuffer  pCommOutput);

SP_STATUS Ssl2SrvGenerateServerVerify(PSPContext pContext,
                              PSPBuffer  pCommOutput);

SP_STATUS Ssl2SrvVerifyClientFinishMsg(PSPContext pContext,
                              PSPBuffer  pCommInput);

#define SSL_OFFSET_OF(t, v) ((DWORD)(ULONG_PTR)&(((t)NULL)->v))


#define SSL2_CERT_TYPE_FROM_CAPI(s) X509_ASN_ENCODING


SP_STATUS WINAPI
Ssl2ServerProtocolHandler(
    PSPContext pContext,
    PSPBuffer pCommInput,
    PSPBuffer pCommOutput)
{
    SP_STATUS pctRet = 0;
    DWORD cMessageType;

    DWORD dwStateTransition;
    BOOL fRaw = TRUE;
    SPBuffer MsgInput;
    DWORD cbMsg;
    PUCHAR pb;
    UCHAR bCT;

    if (NULL != pCommOutput)
    {
        pCommOutput->cbData = 0;
    }

    MsgInput.pvBuffer = pCommInput->pvBuffer;
    MsgInput.cbBuffer = pCommInput->cbBuffer;
    MsgInput.cbData   = pCommInput->cbData;

    // In the following states, we should decrypt the message:


    switch(pContext->State)
    {
        case SSL2_STATE_SERVER_VERIFY:
        case SSL2_STATE_SERVER_RESTART:
            pctRet = Ssl2DecryptMessage(pContext, pCommInput, &MsgInput);
            cMessageType = ((PUCHAR) MsgInput.pvBuffer)[0];
            fRaw = FALSE;
            break;

        case SP_STATE_SHUTDOWN:
        case SP_STATE_SHUTDOWN_PENDING:
            cMessageType = 0;
            break;

        case SP_STATE_CONNECTED:
            // The server has attempted to initiate a reconnect.
            return SP_LOG_RESULT(SEC_E_UNSUPPORTED_FUNCTION);

        default:
            if(pCommInput->cbData < 3)
            {
                return SP_LOG_RESULT(PCT_INT_INCOMPLETE_MSG);
            }
            cMessageType = ((PUCHAR) MsgInput.pvBuffer)[2];
            break;

    }


    if (pctRet != PCT_ERR_OK)
    {
        // to handle incomplete message errors
        return(pctRet);
    }

    dwStateTransition = pContext->State | (cMessageType<<16);




    switch(dwStateTransition)
    {
        case SP_STATE_SHUTDOWN_PENDING:
            // There's no CloseNotify in SSL2, so just transition to
            // the shutdown state and leave the output buffer empty.
            pContext->State = SP_STATE_SHUTDOWN;
            break;

        case SP_STATE_SHUTDOWN:
            return PCT_INT_EXPIRED;

        /* Server receives client hello */
        case (SSL2_MT_CLIENT_HELLO << 16) | SP_STATE_NONE:
        {
            PSsl2_Client_Hello pSsl2Hello;

            // Attempt to recognize and handle various versions of client
            // hello, start by trying to unpickle the most recent version, and
            // then next most recent, until one unpickles.  Then run the handle
            // code.  We can also put unpickling and handling code in here for
            // SSL messages.

            pctRet = Ssl2UnpackClientHello(pCommInput, &pSsl2Hello);

            if (PCT_ERR_OK == pctRet)
            {

                if (((pContext->Flags & CONTEXT_FLAG_NOCACHE) == 0) &&
                    (pSsl2Hello->cbSessionID) &&
                    (SPCacheRetrieveBySession(pContext,
                                              pSsl2Hello->SessionID,
                                              pSsl2Hello->cbSessionID,
                                              &pContext->RipeZombie)))

                {
                    DebugLog((DEB_TRACE, "Accept client's reconnect request.\n"));

                    pctRet = Ssl2SrvGenRestart(pContext,
                                               pSsl2Hello,
                                               pCommOutput);
                    if (PCT_ERR_OK == pctRet)
                    {
                        pContext->State = SSL2_STATE_SERVER_VERIFY;
                    }
                }
                else
                {
                    // We're doing a full handshake, so allocate a cache entry.

                    if(!SPCacheRetrieveNew(TRUE,
                                           pContext->pszTarget, 
                                           &pContext->RipeZombie))
                    {
                        pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
                    }
                    else
                    {
                        pContext->RipeZombie->fProtocol      = pContext->dwProtocol;
                        pContext->RipeZombie->dwCF           = pContext->dwRequestedCF;
                        pContext->RipeZombie->pServerCred    = pContext->pCredGroup;

                        pctRet = Ssl2SrvHandleClientHello(pContext,
                                                          pCommInput,
                                                          pSsl2Hello,
                                                          pCommOutput);
                        if (PCT_ERR_OK == pctRet)
                        {
                            pContext->State = SSL2_STATE_SERVER_HELLO;
                        }
                    }
                }
                SPExternalFree(pSsl2Hello);

            }
            else if(pctRet != PCT_INT_INCOMPLETE_MSG)
            {
                pctRet |= PCT_INT_DROP_CONNECTION;
            }


            if (SP_FATAL(pctRet))
            {
                pContext->State = PCT1_STATE_ERROR;
            }
            break;
        }

        case (SSL2_MT_CLIENT_MASTER_KEY << 16) | SSL2_STATE_SERVER_HELLO:

            pctRet = Ssl2SrvHandleCMKey(pContext, pCommInput, pCommOutput);
            if (SP_FATAL(pctRet))
            {
                pContext->State = PCT1_STATE_ERROR;
            }
            else
            {
                if (PCT_ERR_OK == pctRet)
                {
                    pContext->State = SSL2_STATE_SERVER_VERIFY;
                }
                // We received a non-fatal error, so the state doesn't change,
                // giving the app time to deal with this.
            }
            break;

        case (SSL2_MT_CLIENT_FINISHED_V2 << 16) | SSL2_STATE_SERVER_VERIFY:
            pctRet = Ssl2SrvHandleClientFinish(
                                                pContext,
                                                &MsgInput,
                                                pCommOutput);
            if (SP_FATAL(pctRet))
            {
                pContext->State = PCT1_STATE_ERROR;
            }
            else
            {
            if (PCT_ERR_OK == pctRet)
            {
                pContext->State = SP_STATE_CONNECTED;
                pContext->DecryptHandler = Ssl2DecryptHandler;
                pContext->Encrypt = Ssl2EncryptMessage;
                pContext->Decrypt = Ssl2DecryptMessage;
                pContext->GetHeaderSize = Ssl2GetHeaderSize;

            }
            // We received a non-fatal error, so the state doesn't change,
            // giving the app time to deal with this.
            }
            break;

        default:
            DebugLog((DEB_WARN, "Error in protocol, dwStateTransition is %lx\n", dwStateTransition));
            pContext->State = PCT1_STATE_ERROR;
            pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
            break;
    }

    if (pctRet & PCT_INT_DROP_CONNECTION)
    {
        pContext->State &= ~SP_STATE_CONNECTED;
    }

    return(pctRet);
}



SP_STATUS
Ssl2SrvHandleClientHello(
    PSPContext         pContext,
    PSPBuffer           pCommInput,
    PSsl2_Client_Hello  pHello,
    PSPBuffer           pCommOutput)
{
    SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
    PSPCredential  pCred;
    Ssl2_Server_Hello    Reply;
    DWORD           cCommonCiphers;
    DWORD           CommonCiphers[MAX_UNI_CIPHERS];
    PSessCacheItem  pZombie;
    BOOL            fFound;
    DWORD           i,j;

    SP_BEGIN("Ssl2SrvHandleClientHello");

    pCommOutput->cbData = 0;

    /* validate the buffer configuration */

    if(NULL == pContext)
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
    }
    pZombie = pContext->RipeZombie;

    // See if we have a cert that supports ssl2
    pctRet = SPPickServerCertificate(pContext, SP_EXCH_RSA_PKCS1);
    if(PCT_ERR_OK != pctRet)
    {
        SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION);
    }

    pCred   = pZombie->pActiveServerCred;
    if (!pCred)
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
    }

    do {

        ZeroMemory(&Reply, sizeof(Reply));

        //
        // Calculate common ciphers:
        //

        cCommonCiphers = 0;

        for(i = 0; i < UniNumCiphers; i++)
        {
            PCipherInfo      pCipherInfo;
            PHashInfo        pHashInfo;
            PKeyExchangeInfo pExchInfo;

            // Is this an SSL2 cipher suite?
            if(!(UniAvailableCiphers[i].fProt & pContext->RipeZombie->fProtocol))
            {
                continue;
            }

            pCipherInfo = GetCipherInfo(UniAvailableCiphers[i].aiCipher,
                                        UniAvailableCiphers[i].dwStrength);
            if(NULL == pCipherInfo)
            {
                continue;
            }

            if(!IsCipherSuiteAllowed(pContext,
                                     pCipherInfo,
                                     pZombie->fProtocol,
                                     pZombie->dwCF,
                                     UniAvailableCiphers[i].dwFlags))
            {
                continue;
            }

            pHashInfo = GetHashInfo(UniAvailableCiphers[i].aiHash);
            if(NULL == pHashInfo)
            {
                continue;
            }

            if(!IsHashAllowed(pContext, pHashInfo, pZombie->fProtocol))
            {
                continue;
            }

            pExchInfo = GetKeyExchangeInfo(UniAvailableCiphers[i].KeyExch);
            if(NULL == pExchInfo)
            {
                continue;
            }
            if(!IsExchAllowed(pContext, pExchInfo, pZombie->fProtocol))
            {
                continue;
            }


            // Is this cipher suite supported by the client?
            for(fFound = FALSE, j = 0; j < pHello->cCipherSpecs; j++)
            {
                if(UniAvailableCiphers[i].CipherKind == pHello->CipherSpecs[j])
                {
                    fFound = TRUE;
                    break;
                }
            }
            if(!fFound)
            {
                continue;
            }

            // Does the CSP support this cipher suite?
            if(!IsAlgSupportedCapi(pContext->RipeZombie->fProtocol,
                                   UniAvailableCiphers + i,
                                   pCred->pCapiAlgs,
                                   pCred->cCapiAlgs))
            {
                continue;
            }

            // Add this cipher to list.
            CommonCiphers[cCommonCiphers++] = UniAvailableCiphers[i].CipherKind;
        }

        //
        // if cCommonCipers == 0, then we have none in common.  At this point, we
        // should generate an error response, but that is for later.  For now,
        // we will generate an invalid_token return, and bail out.
        //

        if (cCommonCiphers == 0)
        {
            pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
            LogCipherMismatchEvent();
            break;
        }


        Reply.cCipherSpecs = cCommonCiphers;
        Reply.pCipherSpecs = CommonCiphers;
        Reply.SessionIdHit = 0;

        Reply.CertificateType =   SSL2_CERT_TYPE_FROM_CAPI(pCred->pCert->dwCertEncodingType);

        // Auto allocate the certificate.  !We must free them when we're done....
        Reply.pCertificate = NULL;
        Reply.cbCertificate = 0;
        pctRet = SPSerializeCertificate(SP_PROT_SSL2,
                                        FALSE,
                                        &Reply.pCertificate,
                                        &Reply.cbCertificate,
                                        pCred->pCert,
                                        0);

        if (PCT_ERR_OK != pctRet)
        {
            break;
        }



        /* Generate a conneciton id to use while establishing connection */

        Reply.cbConnectionID = SSL2_GEN_CONNECTION_ID_LEN;
        GenerateRandomBits(  Reply.ConnectionID,
                             Reply.cbConnectionID );

        CopyMemory(pContext->pConnectionID,
                   Reply.ConnectionID,
                   Reply.cbConnectionID);
        pContext->cbConnectionID = Reply.cbConnectionID;


        /* keep challenge around for later */
        CopyMemory( pContext->pChallenge,
                    pHello->Challenge,
                    pHello->cbChallenge);
        pContext->cbChallenge = pHello->cbChallenge;



        pctRet = Ssl2PackServerHello(&Reply, pCommOutput);

        if(Reply.pCertificate)
        {
            SPExternalFree(Reply.pCertificate);
        }

        if (PCT_ERR_OK != pctRet)
        {
            break;
        }
        pContext->WriteCounter = 1;  /* received client hello */
        pContext->ReadCounter = 1;   /* Sending server hello */


        SP_RETURN(PCT_ERR_OK);
    } while (TRUE); /* end Polish Loop */

    if((pContext->Flags & CONTEXT_FLAG_EXT_ERR) &&
        (pctRet == PCT_ERR_SPECS_MISMATCH))
    {
        // Our SSL2 implementation does not do client auth,
        // so there is only one error message, cipher error.
        pCommOutput->cbData = 3; // MSG-ERROR + ERROR-CODE-MSB + ERROR-CODE-LSB

        if(pCommOutput->pvBuffer == NULL)
        {
            pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
            if (NULL == pCommOutput->pvBuffer)
            {
                SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
            }
            pCommOutput->cbBuffer = pCommOutput->cbData;
        }
        if(pCommOutput->cbData > pCommOutput->cbBuffer)
        {
            // Required buffer size returned in pCommOutput->cbData.
            SP_RETURN(SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL));
        }

        ((PUCHAR)pCommOutput->pvBuffer)[0] = SSL2_MT_ERROR;
        ((PUCHAR)pCommOutput->pvBuffer)[1] = MSBOF(SSL_PE_NO_CIPHER);
        ((PUCHAR)pCommOutput->pvBuffer)[2] = LSBOF(SSL_PE_NO_CIPHER);

    }
    SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION);
}



 SP_STATUS
 Ssl2SrvGenRestart(
    PSPContext         pContext,
    PSsl2_Client_Hello  pHello,
    PSPBuffer           pCommOutput)
{
    SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
    SPBuffer SecondOutput;
    Ssl2_Server_Hello    Reply;
    DWORD cbMessage, cbMsg, cPadding;
    PSessCacheItem  pZombie;

    SP_BEGIN("Ssl2SrvGenRestart");

    pCommOutput->cbData = 0;

    /* validate the buffer configuration */

    /* make sure we have the needed authentication data area */
    if (NULL == pContext)
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
    }
    pZombie = pContext->RipeZombie;




    do {

        FillMemory( &Reply, sizeof( Reply ), 0 );

        Reply.SessionIdHit = (DWORD)1;
        Reply.cCipherSpecs = 0;
        Reply.pCipherSpecs = NULL;
        Reply.pCertificate = NULL;
        Reply.cbCertificate = 0;
        Reply.CertificateType = 0;

        /* Note, we generate both a server hello, and a server verify in
         * this handling routing.  This is because netscape will not send
         * us a client finish until the server verify is received
         */


        // Load pending ciphers from cache
        pctRet = ContextInitCiphersFromCache(pContext);

        if(PCT_ERR_OK != pctRet)
        {
            break;
        }

        pctRet = ContextInitCiphers(pContext, TRUE, TRUE);

        if(PCT_ERR_OK != pctRet)
        {
            break;
        }

        Reply.cbConnectionID = SSL2_GEN_CONNECTION_ID_LEN;
        GenerateRandomBits(  Reply.ConnectionID,
                             Reply.cbConnectionID );

        CopyMemory(pContext->pConnectionID,
                   Reply.ConnectionID,
                   Reply.cbConnectionID);
        pContext->cbConnectionID = Reply.cbConnectionID;


        /* keep challenge around for later */
        CopyMemory( pContext->pChallenge,
                    pHello->Challenge,
                    pHello->cbChallenge);
        pContext->cbChallenge = pHello->cbChallenge;


        // Make a new set of session keys.
        pctRet = MakeSessionKeys(pContext,
                                 pContext->RipeZombie->hMasterProv,
                                 pContext->RipeZombie->hMasterKey);
        if(pctRet != PCT_ERR_OK)
        {
            break;
        }

        // Activate session keys.
        pContext->hReadKey          = pContext->hPendingReadKey;
        pContext->hWriteKey         = pContext->hPendingWriteKey;
        pContext->hPendingReadKey   = 0;
        pContext->hPendingWriteKey  = 0;


        /* calc size of the server hello (restart only) */
        cbMessage = Reply.cbConnectionID +
                        Reply.cbCertificate +
                        Reply.cCipherSpecs * sizeof(Ssl2_Cipher_Tuple) +
                        SSL_OFFSET_OF(PSSL2_SERVER_HELLO, VariantData) -
                        sizeof(SSL2_MESSAGE_HEADER);

        pCommOutput->cbData = cbMessage + 2;

        /* calc size of server verify */
        cbMsg  = sizeof(UCHAR) + pContext->cbChallenge;

        cPadding = ((cbMsg+pContext->pHashInfo->cbCheckSum) % pContext->pCipherInfo->dwBlockSize);
        if(cPadding)
        {
            cPadding = pContext->pCipherInfo->dwBlockSize - cPadding;
        }

        pCommOutput->cbData += cbMsg +
                              pContext->pHashInfo->cbCheckSum +
                              cPadding +
                              (cPadding?3:2);


        /* are we allocating our own memory? */
        if(pCommOutput->pvBuffer == NULL) {
            pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
            if (NULL == pCommOutput->pvBuffer)
                SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
            pCommOutput->cbBuffer = pCommOutput->cbData;
        }

        if(pCommOutput->cbData > pCommOutput->cbBuffer)
        {
            // Required buffer size returned in pCommOutput->cbData.
            SP_RETURN(PCT_INT_BUFF_TOO_SMALL);
        }


        pctRet = Ssl2PackServerHello(&Reply, pCommOutput);
        if (PCT_ERR_OK != pctRet)
        {
            break;
        }
        pContext->WriteCounter = 1;  /* received client hello */
        pContext->ReadCounter = 1;   /* Sending server hello */

        /* Now pack the server verify message and encrypt it */
        SecondOutput.pvBuffer = (PUCHAR)pCommOutput->pvBuffer+pCommOutput->cbData;
        SecondOutput.cbBuffer = pCommOutput->cbBuffer-pCommOutput->cbData;


        pctRet = Ssl2SrvGenerateServerVerify(pContext, &SecondOutput);
        if (PCT_ERR_OK != pctRet)
        {
            break;
        }
        pCommOutput->cbData += SecondOutput.cbData;

        SP_RETURN(PCT_ERR_OK);
    } while (TRUE); /* end Polish Loop */
    SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION);
}



SP_STATUS
Ssl2SrvHandleCMKey(
    PSPContext pContext,
    PSPBuffer   pCommInput,
    PSPBuffer   pCommOutput)
{
    SP_STATUS          pctRet = PCT_ERR_ILLEGAL_MESSAGE;
    PSsl2_Client_Master_Key  pMasterKey = NULL;
    DWORD               dwKeyLen;
    DWORD               EncryptedLen;
    DWORD               i;

    DWORD               cbData;
    PSessCacheItem      pZombie;

    SP_BEGIN("Ssl2SrvHandleCMKey");

    pCommOutput->cbData = 0;

    pZombie = pContext->RipeZombie;

    do {

        /* make sure we have the needed authentication data area */

        cbData = pCommInput->cbData;
        pctRet = Ssl2UnpackClientMasterKey(pCommInput, &pMasterKey);


        if (PCT_ERR_OK != pctRet)
        {
            // If it's an incomplete message or something, just return;
            if(!SP_FATAL(pctRet))
            {
                SP_RETURN(pctRet);
            }
            break;
        }

        pctRet = PCT_ERR_ILLEGAL_MESSAGE;


        /* CMK sent cleartext, so we must auto-inc the read counter */
        pContext->ReadCounter++;

        pContext->pCipherInfo = NULL;
        pContext->pHashInfo = NULL;
        pContext->pKeyExchInfo = NULL;

        // Pick a cipher suite

        pctRet = PCT_ERR_SPECS_MISMATCH;
        for(i = 0; i < UniNumCiphers; i++)
        {
            // Is this an SSL2 cipher suite?
            if(!(UniAvailableCiphers[i].fProt & pContext->RipeZombie->fProtocol))
            {
                continue;
            }

            if(UniAvailableCiphers[i].CipherKind != pMasterKey->CipherKind)
            {
                continue;
            }


            pZombie->aiCipher     = UniAvailableCiphers[i].aiCipher;
            pZombie->dwStrength   = UniAvailableCiphers[i].dwStrength;
            pZombie->aiHash       = UniAvailableCiphers[i].aiHash;
            pZombie->SessExchSpec = UniAvailableCiphers[i].KeyExch;

            pctRet = ContextInitCiphersFromCache(pContext);

            if(pctRet != PCT_ERR_OK)
            {
                continue;
            }
            break;
        }

        pctRet = ContextInitCiphers(pContext, TRUE, TRUE);
        if(pctRet != PCT_ERR_OK)
        {
            SP_LOG_RESULT(pctRet);
            break;

        }


        /* Copy over the key args */
        CopyMemory( pZombie->pKeyArgs,
                    pMasterKey->KeyArg,
                    pMasterKey->KeyArgLen );
        pZombie->cbKeyArgs = pMasterKey->KeyArgLen;


        // Store the clear key in the context structure.
        CopyMemory( pZombie->pClearKey,
                    pMasterKey->ClearKey,
                    pMasterKey->ClearKeyLen);
        pZombie->cbClearKey = pMasterKey->ClearKeyLen;


        /* Decrypt the encrypted portion of the master key */
        pctRet = pContext->pKeyExchInfo->System->GenerateServerMasterKey(
                    pContext,
                    pMasterKey->ClearKey,
                    pMasterKey->ClearKeyLen,
                    pMasterKey->pbEncryptedKey,
                    pMasterKey->EncryptedKeyLen);
        if(PCT_ERR_OK != pctRet)
        {
            break;
        }


        SPExternalFree( pMasterKey );
        pMasterKey = NULL;

        // Update keys.
        pContext->hReadKey  = pContext->hPendingReadKey;
        pContext->hWriteKey = pContext->hPendingWriteKey;
        pContext->hPendingReadKey   = 0;
        pContext->hPendingWriteKey  = 0;


        pctRet = Ssl2SrvGenerateServerVerify(pContext, pCommOutput);
        SP_RETURN(pctRet);

    } while(TRUE);

    if (pMasterKey)
    {
        SPExternalFree( pMasterKey );
    }
    if((pContext->Flags & CONTEXT_FLAG_EXT_ERR) &&
        (pctRet == PCT_ERR_SPECS_MISMATCH))
    {
        // Our SSL2 implementation does not do client auth,
        // so there is only one error message, cipher error.
        pCommOutput->cbData = 3; // MSG-ERROR + ERROR-CODE-MSB + ERROR-CODE-LSB

        /* are we allocating our own memory? */
        if(pCommOutput->pvBuffer == NULL)
        {
            pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);

            if (NULL == pCommOutput->pvBuffer)
            {
                SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
            }
            pCommOutput->cbBuffer = pCommOutput->cbData;
        }
        if(pCommOutput->cbData <= pCommOutput->cbBuffer)
        {
            ((PUCHAR)pCommOutput->pvBuffer)[0] = SSL2_MT_ERROR;
            ((PUCHAR)pCommOutput->pvBuffer)[1] = MSBOF(SSL_PE_NO_CIPHER);
            ((PUCHAR)pCommOutput->pvBuffer)[2] = LSBOF(SSL_PE_NO_CIPHER);
        }
        else
        {
            pCommOutput->cbData = 0;
        }

    }
    SP_RETURN((PCT_INT_DROP_CONNECTION | pctRet));
}



SP_STATUS
Ssl2SrvVerifyClientFinishMsg(
    PSPContext pContext,
    PSPBuffer  pCommInput)
{
    SP_STATUS     pctRet = PCT_ERR_ILLEGAL_MESSAGE;
    PSSL2_CLIENT_FINISHED pFinished;

    SP_BEGIN("Ssl2SrvVerifyClientFinishMsg");


    /* Note, there is no header in this message, as it has been pre-decrypted */
    if (pCommInput->cbData != sizeof(UCHAR) + pContext->cbConnectionID)
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG));
    }
    pFinished = pCommInput->pvBuffer;
    if (pFinished->MessageId != SSL2_MT_CLIENT_FINISHED_V2)
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG));
    }

    if ( memcmp(pFinished->ConnectionID,
            pContext->pConnectionID,
            pContext->cbConnectionID))
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG));
    }
    SP_RETURN(PCT_ERR_OK);

}

SP_STATUS
Ssl2SrvGenerateServerVerify(
    PSPContext pContext,
    PSPBuffer   pCommOutput)
{
    SP_STATUS     pctRet = PCT_ERR_ILLEGAL_MESSAGE;
    PSSL2_SERVER_VERIFY     pVerify;
    DWORD                   HeaderSize;
    SPBuffer                MsgOutput;
    DWORD                   cPadding;
    BOOL                    fAlloced = FALSE;

    pCommOutput->cbData = 0;

    SP_BEGIN("Ssl2SrvGenerateServerVerify");

    do {

        MsgOutput.cbData = sizeof(UCHAR) + pContext->cbChallenge;
        cPadding = ((MsgOutput.cbData+pContext->pHashInfo->cbCheckSum) % pContext->pCipherInfo->dwBlockSize);
        if(cPadding)
        {
            cPadding = pContext->pCipherInfo->dwBlockSize - cPadding;
        }

        HeaderSize = (cPadding?3:2);

        pCommOutput->cbData = MsgOutput.cbData +
                              pContext->pHashInfo->cbCheckSum +
                              cPadding + HeaderSize;


        /* are we allocating our own memory? */
        if (pCommOutput->pvBuffer == NULL) {
            pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
            if (NULL == pCommOutput->pvBuffer)
            {
                SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
            }
            fAlloced = TRUE;
            pCommOutput->cbBuffer = pCommOutput->cbData;
        }

        MsgOutput.pvBuffer= (PUCHAR)pCommOutput->pvBuffer +
                             HeaderSize+pContext->pHashInfo->cbCheckSum;

        MsgOutput.cbBuffer=  pCommOutput->cbBuffer -
                             HeaderSize-pContext->pHashInfo->cbCheckSum;


        pVerify = (PSSL2_SERVER_VERIFY) MsgOutput.pvBuffer;
        pVerify->MessageId = SSL2_MT_SERVER_VERIFY;


        CopyMemory( pVerify->ChallengeData,
                pContext->pChallenge,
                pContext->cbChallenge );


        pctRet = Ssl2EncryptMessage( pContext, &MsgOutput, pCommOutput);
        if(PCT_ERR_OK != pctRet)
        {
            break;
        }
        SP_RETURN(PCT_ERR_OK);

    } while(TRUE);

    if(fAlloced && (NULL != pCommOutput->pvBuffer))
    {
        SPExternalFree(pCommOutput->pvBuffer);
        pCommOutput->cbBuffer = 0;
        pCommOutput->cbData = 0;
        pCommOutput->pvBuffer = NULL;

    }
    SP_RETURN(PCT_INT_DROP_CONNECTION | pctRet);
}

SP_STATUS
Ssl2SrvGenerateServerFinish(
    PSPContext pContext,
    PSPBuffer   pCommOutput)
{
    SP_STATUS     pctRet = PCT_ERR_ILLEGAL_MESSAGE;
    PSSL2_SERVER_FINISHED pFinish;
    DWORD                   HeaderSize;
    SPBuffer                MsgOutput;
    DWORD                   cPadding;
    BOOL                    fAlloced = FALSE;
    pCommOutput->cbData = 0;
    SP_BEGIN("Ssl2SrvGenerateServerFinish");

    do {

        /* Generate a session id to use during the session */
        pContext->RipeZombie->cbSessionID = SSL2_SESSION_ID_LEN;

        /* store this context in the cache */
        /* note - we don't check error 'cause it's recoverable
         * if we don't cache */

        SPCacheAdd(pContext);

        MsgOutput.cbData = sizeof(UCHAR) + pContext->RipeZombie->cbSessionID;
        cPadding = ((MsgOutput.cbData+pContext->pHashInfo->cbCheckSum) % pContext->pCipherInfo->dwBlockSize);
        if(cPadding)
        {
            cPadding = pContext->pCipherInfo->dwBlockSize - cPadding;
        }

        HeaderSize = (cPadding?3:2);

        pCommOutput->cbData = MsgOutput.cbData +
                              pContext->pHashInfo->cbCheckSum +
                              cPadding +
                              HeaderSize;

        /* are we allocating our own memory? */
        if(pCommOutput->pvBuffer == NULL)
        {
            pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
            if (NULL == pCommOutput->pvBuffer)
            {
                SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
            }
            fAlloced = TRUE;

            pCommOutput->cbBuffer = pCommOutput->cbData;
        }
        if(pCommOutput->cbData > pCommOutput->cbBuffer)
        {
            // Required buffer size returned in pCommOutput->cbData.
            SP_RETURN(SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL));
        }

        MsgOutput.pvBuffer= (PUCHAR)pCommOutput->pvBuffer + HeaderSize+pContext->pHashInfo->cbCheckSum;
        MsgOutput.cbBuffer=  pCommOutput->cbBuffer-HeaderSize-pContext->pHashInfo->cbCheckSum;


        pFinish = (PSSL2_SERVER_FINISHED) MsgOutput.pvBuffer;
        pFinish->MessageId = SSL2_MT_SERVER_FINISHED_V2;


        CopyMemory( pFinish->SessionID,
                pContext->RipeZombie->SessionID,
                pContext->RipeZombie->cbSessionID );

        /* Cache Context Here */

        pctRet = Ssl2EncryptMessage( pContext, &MsgOutput, pCommOutput);
        if(PCT_ERR_OK != pctRet)
        {
            break;
        }

        SP_RETURN(PCT_ERR_OK);

    } while(TRUE);
    if(fAlloced && (NULL != pCommOutput->pvBuffer))
    {
        SPExternalFree(pCommOutput->pvBuffer);
        pCommOutput->cbBuffer = 0;
        pCommOutput->cbData = 0;
        pCommOutput->pvBuffer = NULL;

    }
    SP_RETURN(PCT_INT_DROP_CONNECTION | pctRet);

 }

SP_STATUS
Ssl2SrvHandleClientFinish(
    PSPContext pContext,
    PSPBuffer   pCommInput,
    PSPBuffer   pCommOutput)
{
    SP_STATUS     pctRet = PCT_ERR_ILLEGAL_MESSAGE;

    SP_BEGIN("Ssl2SrvHandleClientFinish");

    pCommOutput->cbData = 0;

    pctRet = Ssl2SrvVerifyClientFinishMsg(pContext, pCommInput);
    if (PCT_ERR_OK != pctRet)
    {
        SP_RETURN(pctRet);
    }
    pctRet = Ssl2SrvGenerateServerFinish(pContext, pCommOutput);

    SP_RETURN(pctRet);
}