//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995.
//
//  File:       ssl3msg.c
//
//  Contents:   Main crypto functions for SSL3.
//
//  Classes:
//
//  Functions:
//
//  History:    04-16-96   ramas    Created.
//
//----------------------------------------------------------------------------

#include <spbase.h>

#if VERIFYHASH
BYTE  rgbF[5000];
DWORD ibF = 0;
#endif


//------------------------------------------------------------------------------------------

SP_STATUS WINAPI
Ssl3DecryptHandler(
    PSPContext pContext,
    PSPBuffer pCommInput,
    PSPBuffer pAppOutput)
{
    SP_STATUS pctRet = PCT_ERR_OK;

    if(pCommInput->cbData == 0)
    {
        return PCT_INT_INCOMPLETE_MSG;
    }

    if(!(pContext->State & SP_STATE_CONNECTED) || pContext->Decrypt == NULL)
    {
        return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
    }

    switch(*(PBYTE)pCommInput->pvBuffer)
    {
    case SSL3_CT_HANDSHAKE:
        if(pContext->RipeZombie->fProtocol & SP_PROT_CLIENTS)
        {
            // This should be a HelloRequest message. We should make sure, and
            // then completely consume the message.
            pctRet = pContext->Decrypt( pContext,
                                        pCommInput,  // message
                                        pAppOutput);    // Unpacked Message
            if(PCT_ERR_OK != pctRet)
            {
                return pctRet;
            }

            if(*(PBYTE)pAppOutput->pvBuffer != SSL3_HS_HELLO_REQUEST ||
               pAppOutput->cbData != sizeof(SHSH))
            {
                // This ain't no HelloRequest!
                return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
            }
        }
        else
        {
            // This is probably a ClientHello message. In any case, let the
            // caller deal with it (by passing it to the LSA process).
            pCommInput->cbData = 0;
        }

        pAppOutput->cbData = 0;

        pContext->State = SSL3_STATE_RENEGOTIATE;
        return SP_LOG_RESULT(PCT_INT_RENEGOTIATE);


    case SSL3_CT_ALERT:
        pctRet = pContext->Decrypt( pContext,
                                    pCommInput,
                                    pAppOutput);
        if(PCT_ERR_OK != pctRet)
        {
            return pctRet;
        }

        pctRet = ParseAlertMessage(pContext,
                                   (PBYTE)pAppOutput->pvBuffer,
                                   pAppOutput->cbData);

        // make sure that APP doesn't see Alert messages...
        pAppOutput->cbData = 0;
        
        return pctRet;


    case SSL3_CT_APPLICATIONDATA:
        pctRet = pContext->Decrypt( pContext,
                                    pCommInput,
                                    pAppOutput);

        return pctRet;


    default:
        return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
    }
}

SP_STATUS WINAPI
Ssl3GetHeaderSize(
    PSPContext pContext,
    PSPBuffer pCommInput,
    DWORD * pcbHeaderSize)
{
    if(pcbHeaderSize == NULL)
    {
        return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
    }

    *pcbHeaderSize = sizeof(SWRAP);
    return PCT_ERR_OK;
}



#if   DBG
BYTE rgb3Mac[2048];
DWORD ibMac = 0;
#endif

//+---------------------------------------------------------------------------
//
//  Function:   Ssl3ComputeMac
//
//  Synopsis:
//
//  Arguments:  [pContext]      --
//              [fReadMac]      --
//              [pClean]        --
//              [cContentType]  --
//              [pbMac]         --
//              [cbMac]
//
//  History:    10-03-97   jbanes   Created.
//
//  Notes:
//
//----------------------------------------------------------------------------
SP_STATUS
Ssl3ComputeMac(
    PSPContext  pContext,
    BOOL        fReadMac,
    PSPBuffer   pClean,
    CHAR        cContentType,
    PBYTE       pbMac,
    DWORD       cbMac)
{
    HCRYPTHASH  hHash = 0;
    DWORD       dwReverseSequence;
    WORD        wReverseData;
    UCHAR       rgbDigest[SP_MAX_DIGEST_LEN];
    DWORD       cbDigest;
    BYTE        rgbPad[CB_SSL3_MAX_MAC_PAD];
    WORD        cbPad;
    HCRYPTPROV  hProv;
    HCRYPTKEY   hSecret;
    DWORD       dwSequence;
    DWORD       dwCapiFlags;
    PHashInfo   pHashInfo;
    SP_STATUS   pctRet;

    if(fReadMac)
    {
        hProv      = pContext->hReadProv;
        hSecret    = pContext->hReadMAC;
        dwSequence = pContext->ReadCounter;
        pHashInfo  = pContext->pReadHashInfo;
    }
    else
    {
        hProv      = pContext->hWriteProv;
        hSecret    = pContext->hWriteMAC;
        dwSequence = pContext->WriteCounter;
        pHashInfo  = pContext->pWriteHashInfo;
    }
    dwCapiFlags = pContext->RipeZombie->dwCapiFlags;

    if(!hProv)
    {
        return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
    }

    // Determine size of pad_1 and pad_2.
    if(pHashInfo->aiHash == CALG_MD5)
    {
        cbPad = CB_SSL3_MD5_MAC_PAD;
    }
    else
    {
        cbPad = CB_SSL3_SHA_MAC_PAD;
    }

    //
    // hash(MAC_write_secret + pad_2 +
    //      hash(MAC_write_secret + pad_1 + seq_num +
    //           SSLCompressed.type + SSLCompressed.length +
    //           SSLCompressed.fragment));
    //

    // Create hash
    if(!SchCryptCreateHash(hProv,
                           pHashInfo->aiHash,
                           0,
                           0,
                           &hHash,
                           dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    // Hash secret
    if(!SchCryptHashSessionKey(hHash,
                               hSecret,
                               CRYPT_LITTLE_ENDIAN,
                               dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    // hash pad 1
    FillMemory(rgbPad, cbPad, PAD1_CONSTANT);
    if(!SchCryptHashData(hHash, rgbPad, cbPad, 0, dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    /* add count */
    dwReverseSequence = 0;
    if(!SchCryptHashData(hHash,
                         (PUCHAR)&dwReverseSequence,
                         sizeof(DWORD),
                         0,
                         dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    dwReverseSequence = htonl(dwSequence);
    if(!SchCryptHashData(hHash,
                         (PUCHAR)&dwReverseSequence,
                         sizeof(DWORD),
                         0,
                         dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    // Add content type.
    if(cContentType != 0)
    {
        if(!SchCryptHashData(hHash, &cContentType, 1, 0, dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
    }

    /* add length */
    wReverseData = (WORD)pClean->cbData >> 8 | (WORD)pClean->cbData << 8;
    if(!SchCryptHashData(hHash,
                         (PBYTE)&wReverseData,
                         sizeof(WORD),
                         0,
                         dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    /* add data */
    if(!SchCryptHashData(hHash,
                         pClean->pvBuffer,
                         pClean->cbData,
                         0,
                         dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    #if VERIFYHASH
        if(ibMac > 1800) ibMac = 0;
        CopyMemory(&rgb3Mac[ibMac], (BYTE *)&dw32High, sizeof(DWORD));
        ibMac += sizeof(DWORD);
        CopyMemory(&rgb3Mac[ibMac], (BYTE *)&dwReverseSeq, sizeof(DWORD));
        ibMac += sizeof(DWORD);
        CopyMemory(&rgb3Mac[ibMac], (BYTE *)&wDataReverse, sizeof(WORD));
        ibMac += sizeof(WORD);
        if(wData < 50)
        {
            CopyMemory(&rgb3Mac[ibMac], (PUCHAR)pClean->pvBuffer, wData);
            ibMac += wData;
        }
    #endif

    // Get inner hash value.
    cbDigest = sizeof(rgbDigest);
    if(!SchCryptGetHashParam(hHash,
                             HP_HASHVAL,
                             rgbDigest,
                             &cbDigest,
                             0,
                             dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    SP_ASSERT(pHashInfo->cbCheckSum == cbDigest);

    SchCryptDestroyHash(hHash, dwCapiFlags);
    hHash = 0;

    #if VERIFYHASH
        CopyMemory(&rgb3Mac[ibMac], rgbDigest, pHashInfo->cbCheckSum);
        ibMac += pHashInfo->cbCheckSum;
    #endif



    // Create hash
    if(!SchCryptCreateHash(hProv,
                           pHashInfo->aiHash,
                           0,
                           0,
                           &hHash,
                           dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    // Hash secret
    if(!SchCryptHashSessionKey(hHash,
                               hSecret,
                               CRYPT_LITTLE_ENDIAN,
                               dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    // hash pad 2
    FillMemory(rgbPad, cbPad, PAD2_CONSTANT);
    if(!SchCryptHashData(hHash, rgbPad, cbPad, 0, dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    if(!SchCryptHashData(hHash, rgbDigest, cbDigest, 0, dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    // Get outer hash value.
    cbDigest = sizeof(rgbDigest);
    if(!SchCryptGetHashParam(hHash,
                             HP_HASHVAL,
                             rgbDigest,
                             &cbDigest,
                             0,
                             dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    SP_ASSERT(pHashInfo->cbCheckSum == cbDigest);

    SchCryptDestroyHash(hHash, dwCapiFlags);
    hHash = 0;

    #if VERIFYHASH
        CopyMemory(&rgb3Mac[ibMac], rgbDigest, pHashInfo->cbCheckSum);
        ibMac += pHashInfo->cbCheckSum;
    #endif

    CopyMemory(pbMac, rgbDigest, cbDigest);

    pctRet = PCT_ERR_OK;

cleanup:

    if(hHash)
    {
        SchCryptDestroyHash(hHash, dwCapiFlags);
    }

    return pctRet;
}


//+---------------------------------------------------------------------------
//
//  Function:   Ssl3BuildFinishMessage
//
//  Synopsis:
//
//  Arguments:  [pContext]      --
//              [pbMd5Digest]   --
//              [pbSHADigest]   --
//              [fClient]       --
//
//  History:    10-03-97   jbanes   Added server-side CAPI integration.
//
//  Notes:
//
//----------------------------------------------------------------------------
SP_STATUS
Ssl3BuildFinishMessage(
    PSPContext pContext,
    BYTE *pbMd5Digest,
    BYTE *pbSHADigest,
    BOOL fClient)
{
    BYTE rgbPad1[CB_SSL3_MAX_MAC_PAD];
    BYTE rgbPad2[CB_SSL3_MAX_MAC_PAD];
    BYTE szClnt[] = "CLNT";
    BYTE szSrvr[] = "SRVR";
    DWORD cbMessage;
    HCRYPTHASH hHash = 0;
    DWORD cbDigest;
    SP_STATUS pctRet;

    //
    // Compute the two hash values as follows:
    //
    // enum { client(0x434c4e54), server(0x53525652) } Sender;
    // enum { client("CLNT"), server("SRVR") } Sender;
    //
    // struct {
    //     opaque md5_hash[16];
    //     opaque sha_hash[20];
    // } Finished;
    //
    // md5_hash  -  MD5(master_secret + pad2 + MD5(handshake_messages +
    //      Sender + master_secret + pad1))
    //
    // sha_hash  -  SHA(master_secret + pad2 + SHA(handshake_messages +
    //      Sender + master_secret + pad1))
    //
    // pad_1 - The character 0x36 repeated 48 times for MD5 or
    //         40 times for SHA.
    //
    // pad_2 - The character 0x5c repeated the same number of times.
    //

    FillMemory(rgbPad1, sizeof(rgbPad1), PAD1_CONSTANT);
    FillMemory(rgbPad2, sizeof(rgbPad2), PAD2_CONSTANT);


    // Make local copy of the handshake_messages MD5 hash object
    if(!SchCryptDuplicateHash(pContext->hMd5Handshake,
                              NULL,
                              0,
                              &hHash,
                              pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    // Add rest of stuff to local MD5 hash object.
    if(!SchCryptHashData(hHash,
                         fClient ? szClnt : szSrvr,
                         4,
                         0,
                         pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    if(!SchCryptHashSessionKey(hHash,
                               pContext->RipeZombie->hMasterKey,
                               CRYPT_LITTLE_ENDIAN,
                               pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    if(!SchCryptHashData(hHash,
                         rgbPad1,
                         CB_SSL3_MD5_MAC_PAD,
                         0,
                         pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    cbDigest = CB_MD5_DIGEST_LEN;
    if(!SchCryptGetHashParam(hHash,
                             HP_HASHVAL,
                             pbMd5Digest,
                             &cbDigest,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
    hHash = 0;

    // Compute "parent" MD5 hash
    if(!SchCryptCreateHash(pContext->RipeZombie->hMasterProv,
                           CALG_MD5,
                           0,
                           0,
                           &hHash,
                           pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    if(!SchCryptHashSessionKey(hHash,
                               pContext->RipeZombie->hMasterKey,
                               CRYPT_LITTLE_ENDIAN,
                               pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    if(!SchCryptHashData(hHash,
                         rgbPad2,
                         CB_SSL3_MD5_MAC_PAD,
                         0,
                         pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    if(!SchCryptHashData(hHash,
                         pbMd5Digest,
                         CB_MD5_DIGEST_LEN,
                         0,
                         pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    cbDigest = CB_MD5_DIGEST_LEN;
    if(!SchCryptGetHashParam(hHash,
                             HP_HASHVAL,
                             pbMd5Digest,
                             &cbDigest,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
    hHash = 0;

    // Build SHA Hash

    // Make local copy of the handshake_messages SHA hash object
    if(!SchCryptDuplicateHash(pContext->hShaHandshake,
                              NULL,
                              0,
                              &hHash,
                              pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }

    // SHA(handshake_messages + Sender + master_secret + pad1)
    if(!SchCryptHashData(hHash,
                         fClient ? szClnt : szSrvr,
                         4,
                         0,
                         pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    if(!SchCryptHashSessionKey(hHash,
                               pContext->RipeZombie->hMasterKey,
                               CRYPT_LITTLE_ENDIAN,
                               pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    if(!SchCryptHashData(hHash,
                         rgbPad1,
                         CB_SSL3_SHA_MAC_PAD,
                         0,
                         pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    cbDigest = A_SHA_DIGEST_LEN;
    if(!SchCryptGetHashParam(hHash,
                             HP_HASHVAL,
                             pbSHADigest,
                             &cbDigest,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
    hHash = 0;

    // SHA(master_secret + pad2 + SHA-hash);
    if(!SchCryptCreateHash(pContext->RipeZombie->hMasterProv,
                           CALG_SHA,
                           0,
                           0,
                           &hHash,
                           pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    if(!SchCryptHashSessionKey(hHash,
                               pContext->RipeZombie->hMasterKey,
                               CRYPT_LITTLE_ENDIAN,
                               pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    if(!SchCryptHashData(hHash,
                         rgbPad2,
                         CB_SSL3_SHA_MAC_PAD,
                         0,
                         pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    if(!SchCryptHashData(hHash,
                         pbSHADigest,
                         A_SHA_DIGEST_LEN,
                         0,
                         pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    cbDigest = A_SHA_DIGEST_LEN;
    if(!SchCryptGetHashParam(hHash,
                             HP_HASHVAL,
                             pbSHADigest,
                             &cbDigest,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto cleanup;
    }
    SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
        hHash = 0;

    pctRet = PCT_ERR_OK;

cleanup:

    if(hHash)
    {
        SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
    }

    return pctRet;
}


/*****************************************************************************/
DWORD Ssl3CiphertextLen(
    PSPContext pContext,
    DWORD cbMessage,
    BOOL fClientIsSender)
{
    DWORD cbBlock;

    // Abort early if we're not encrypting.
    if(pContext->pWriteCipherInfo == NULL)
    {
        // Add record header length.
        cbMessage += sizeof(SWRAP);

        return cbMessage;
    }

    // Add MAC length.
    cbMessage += pContext->pWriteHashInfo->cbCheckSum;

    // Add padding if we're using a block cipher.
    cbBlock = pContext->pWriteCipherInfo->dwBlockSize;
    if(cbBlock > 1)
    {
        cbMessage += cbBlock - cbMessage % cbBlock;
    }

    // Add record header length.
    cbMessage += sizeof(SWRAP);

    return cbMessage;
}

//+---------------------------------------------------------------------------
//
//  Function:   Ssl3EncryptRaw
//
//  Synopsis:   Perform the MAC and encryption steps on an SSL3 record.
//
//  Arguments:  [pContext]      --  Schannel context.
//              [pAppInput]     --  Data to be encrypted.
//              [pCommOutput]   --  (output) Encrypted SSL3 record.
//              [bContentType]  --  SSL3 context type.
//
//  History:    10-22-97   jbanes   CAPI integrated.
//
//  Notes:      This function doesn't touch the header portion of the SSL3
//              record. This is handle by the calling function.
//
//----------------------------------------------------------------------------
SP_STATUS WINAPI
Ssl3EncryptRaw(
    PSPContext pContext,
    PSPBuffer  pAppInput,
    PSPBuffer  pCommOutput,
    BYTE       bContentType)
{
    SP_STATUS pctRet;
    SPBuffer Clean;
    SPBuffer Encrypted;
    DWORD cbBlock;
    DWORD cbPadding;
    PUCHAR pbMAC = NULL;
    BOOL   fIsClient = FALSE;
    DWORD  cbBuffExpected;

    if((pContext == NULL) ||
        (pContext->RipeZombie == NULL) ||
        (pContext->pWriteHashInfo == NULL) ||
        (pContext->pWriteCipherInfo == NULL) ||
        (pAppInput == NULL) ||
        (pCommOutput == NULL) ||
        (pAppInput->pvBuffer == NULL) ||
        (pCommOutput->pvBuffer == NULL))
    {
        return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
    }

    if(pAppInput->cbData > pAppInput->cbBuffer)
    {
        return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
    }
    fIsClient = ( 0 != (pContext->RipeZombie->fProtocol & SP_PROT_SSL3TLS1_CLIENTS));

    cbBuffExpected = Ssl3CiphertextLen(pContext, pAppInput->cbData, fIsClient);
    if(cbBuffExpected == 0)
    {
        return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
    }

    if(pCommOutput->cbBuffer < cbBuffExpected)
    {
        return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
    }

    Clean.cbData   = pAppInput->cbData;
    Clean.pvBuffer = (PUCHAR)pCommOutput->pvBuffer + sizeof(SWRAP);
    Clean.cbBuffer = pCommOutput->cbBuffer - sizeof(SWRAP);

    /* Move data out of the way if necessary */
    if(Clean.pvBuffer != pAppInput->pvBuffer)
    {
        DebugLog((DEB_WARN, "SSL3EncryptRaw: Unnecessary Move, performance hog\n"));
        MoveMemory(Clean.pvBuffer,
                   pAppInput->pvBuffer,
                   pAppInput->cbData);
    }

    // Transfer the write key over from the application process.
    if(pContext->hWriteKey == 0 &&
       pContext->pWriteCipherInfo->aiCipher != CALG_NULLCIPHER)
    {
        DebugLog((DEB_TRACE, "Transfer write key from user process.\n"));
        pctRet = SPGetUserKeys(pContext, SCH_FLAG_WRITE_KEY);
        if(pctRet != PCT_ERR_OK)
        {
            return SP_LOG_RESULT(pctRet);
        }
    }

    // Compute MAC and add it to end of message.
    pbMAC = (PUCHAR)Clean.pvBuffer + Clean.cbData;
    if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3)
    {
        pctRet = Ssl3ComputeMac(pContext,
                                FALSE,
                                &Clean,
                                bContentType,
                                pbMAC,
                                pContext->pWriteHashInfo->cbCheckSum);
        if(pctRet != PCT_ERR_OK)
        {
            return pctRet;
        }
    }
    else
    {
        pctRet = Tls1ComputeMac(pContext,
                                FALSE,
                                &Clean,
                                bContentType,
                                pbMAC,
                                pContext->pWriteHashInfo->cbCheckSum);
        if(pctRet != PCT_ERR_OK)
        {
            return pctRet;
        }
    }
    Clean.cbData += pContext->pWriteHashInfo->cbCheckSum;

    pContext->WriteCounter++;

    // Add block cipher padding to end of message.
    cbBlock = pContext->pWriteCipherInfo->dwBlockSize;
    if(cbBlock > 1)
    {
        // This is a block cipher.
        cbPadding = cbBlock - Clean.cbData % cbBlock;

        FillMemory((PUCHAR)Clean.pvBuffer + Clean.cbData,
                   cbPadding,
                   (UCHAR)(cbPadding - 1));
        Clean.cbData += cbPadding;
    }

    SP_ASSERT(Clean.cbData <= Clean.cbBuffer);

    Encrypted.cbData   = Clean.cbData;
    Encrypted.pvBuffer = Clean.pvBuffer;
    Encrypted.cbBuffer = Clean.cbBuffer;

    // Encrypt message.
    if(pContext->pWriteCipherInfo->aiCipher != CALG_NULLCIPHER)
    {
        if(!SchCryptEncrypt(pContext->hWriteKey,
                            0, FALSE, 0,
                            Encrypted.pvBuffer,
                            &Encrypted.cbData,
                            Encrypted.cbBuffer,
                            pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            return PCT_INT_INTERNAL_ERROR;
        }
    }

    pCommOutput->cbData = Encrypted.cbData + sizeof(SWRAP);

    return PCT_ERR_OK;
}

//+---------------------------------------------------------------------------
//
//  Function:   Ssl3EncryptMessage
//
//  Synopsis:   Encode a block of data as an SSL3 record.
//
//  Arguments:  [pContext]      --  Schannel context.
//              [pAppInput]     --  Data to be encrypted.
//              [pCommOutput]   --  (output) Completed SSL3 record.
//
//  History:    10-22-97   jbanes   CAPI integrated.
//
//  Notes:      An SSL3 record is formatted as:
//
//                  BYTE header[5];
//                  BYTE data[pAppInput->cbData];
//                  BYTE mac[mac_size];
//                  BYTE padding[padding_size];
//
//----------------------------------------------------------------------------
SP_STATUS WINAPI
Ssl3EncryptMessage( PSPContext pContext,
                    PSPBuffer   pAppInput,
                    PSPBuffer   pCommOutput)
{
    DWORD cbMessage;
    SP_STATUS pctRet;

    SP_BEGIN("Ssl3EncryptMessage");

    if((pContext == NULL) ||
        (pContext->RipeZombie == NULL) ||
        (pAppInput == NULL) ||
        (pCommOutput == NULL) ||
        (pCommOutput->pvBuffer == NULL))
    {
        return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
    }

    DebugLog((DEB_TRACE, "Input: cbData:0x%x, cbBuffer:0x%x, pvBuffer:0x%8.8x\n",
        pAppInput->cbData,
        pAppInput->cbBuffer,
        pAppInput->pvBuffer));

    DebugLog((DEB_TRACE, "Output: cbData:0x%x, cbBuffer:0x%x, pvBuffer:0x%8.8x\n",
        pCommOutput->cbData,
        pCommOutput->cbBuffer,
        pCommOutput->pvBuffer));

    // Compute encrypted message size.
    cbMessage = Ssl3CiphertextLen(pContext, pAppInput->cbData, TRUE);

    pctRet = Ssl3EncryptRaw(pContext, pAppInput, pCommOutput, SSL3_CT_APPLICATIONDATA);
    if(pctRet != PCT_ERR_OK)
    {
        SP_RETURN(SP_LOG_RESULT(pctRet));
    }

    SetWrapNoEncrypt(pCommOutput->pvBuffer,
                     SSL3_CT_APPLICATIONDATA,
                     cbMessage - sizeof(SWRAP));
    if(pContext->RipeZombie->fProtocol & SP_PROT_TLS1)
    {
        ((PUCHAR)pCommOutput->pvBuffer)[02] = TLS1_CLIENT_VERSION_LSB;
    }

    DebugLog((DEB_TRACE, "Output: cbData:0x%x, cbBuffer:0x%x, pvBuffer:0x%8.8x\n",
        pCommOutput->cbData,
        pCommOutput->cbBuffer,
        pCommOutput->pvBuffer));

    SP_RETURN(PCT_ERR_OK);
}


//+---------------------------------------------------------------------------
//
//  Function:   Ssl3DecryptMessage
//
//  Synopsis:   Decode an SSL3 record.
//
//  Arguments:  [pContext]      --  Schannel context.
//              [pMessage]      --  Data from the remote party.
//              [pAppOutput]    --  (output) Decrypted data.
//
//  History:    10-22-97   jbanes   CAPI integrated.
//
//  Notes:      The number of input data bytes consumed by this function
//              is returned in pMessage->cbData.
//
//----------------------------------------------------------------------------
SP_STATUS WINAPI
Ssl3DecryptMessage( PSPContext         pContext,
                    PSPBuffer          pMessage,
                    PSPBuffer          pAppOutput)
{
    SP_STATUS pctRet;
    SPBuffer  Clean;
    SPBuffer  Encrypted;
    UCHAR     rgbDigest[SP_MAX_DIGEST_LEN];
    PUCHAR    pbMAC;
    DWORD     dwLength, cbActualData;
    SWRAP     *pswrap = pMessage->pvBuffer;
    DWORD     dwVersion;

    DWORD cbBlock;
    DWORD cbPadding;

    SP_BEGIN("Ssl3DecryptMessage");

    if((pContext == NULL) ||
        (pContext->pReadCipherInfo == NULL) ||
        (pContext->RipeZombie == NULL) ||
        (pAppOutput == NULL) ||
        (pMessage == NULL) ||
        (pMessage->pvBuffer == NULL))
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
    }

    /* First determine the length of data, the length of padding,
     * and the location of data, and the location of MAC */
    cbActualData = pMessage->cbData;
    pMessage->cbData = sizeof(SWRAP); /* minimum amount of data we need */

    if(cbActualData < sizeof(SWRAP))
    {
        SP_RETURN(PCT_INT_INCOMPLETE_MSG);
    }

    dwVersion = COMBINEBYTES(pswrap->bMajor, pswrap->bMinor);
    if(dwVersion != SSL3_CLIENT_VERSION && dwVersion != TLS1_CLIENT_VERSION)
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG));
    }

    dwLength = COMBINEBYTES(pswrap->bcbMSBSize, pswrap->bcbLSBSize);

    Encrypted.pvBuffer = (PUCHAR)pMessage->pvBuffer + sizeof(SWRAP);
    Encrypted.cbBuffer = pMessage->cbBuffer - sizeof(SWRAP);

    pMessage->cbData += dwLength ;

    if(pMessage->cbData > cbActualData)
    {
        SP_RETURN(PCT_INT_INCOMPLETE_MSG);
    }

    Encrypted.cbData = dwLength; /* encrypted data size */

    SP_ASSERT(Encrypted.cbData != 0);

    /* check to see if we have a block size violation */
    if(Encrypted.cbData % pContext->pReadCipherInfo->dwBlockSize)
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_MSG_ALTERED));
    }

    // Transfer the read key over from the application process.
    if(pContext->hReadKey == 0 && 
       pContext->pReadCipherInfo->aiCipher != CALG_NULLCIPHER)
    {
        DebugLog((DEB_TRACE, "Transfer read key from user process.\n"));
        pctRet = SPGetUserKeys(pContext, SCH_FLAG_READ_KEY);
        if(pctRet != PCT_ERR_OK)
        {
            return SP_LOG_RESULT(pctRet);
        }
    }

    // Decrypt message.
    if(pContext->pReadCipherInfo->aiCipher != CALG_NULLCIPHER)
    {
        if(!SchCryptDecrypt(pContext->hReadKey,
                            0, FALSE, 0,
                            Encrypted.pvBuffer,
                            &Encrypted.cbData,
                            pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            SP_RETURN(PCT_INT_INTERNAL_ERROR);
        }
    }

    // Remove block cipher padding.
    cbBlock = pContext->pReadCipherInfo->dwBlockSize;
    if(cbBlock > 1)
    {
        // This is a block cipher.
        cbPadding = *((PUCHAR)Encrypted.pvBuffer + Encrypted.cbData - 1) + 1;

        if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3)
        {
            if(cbPadding > cbBlock || cbPadding >= Encrypted.cbData)
            {
                // Invalid pad size.
                DebugLog((DEB_WARN, "FINISHED Message: Padding Invalid\n"));
                SP_RETURN(SP_LOG_RESULT(PCT_INT_MSG_ALTERED));
            }
        }
        else
        {
            if(cbPadding > 256 || cbPadding >= Encrypted.cbData)
            {
                // Invalid pad size.
                DebugLog((DEB_WARN, "FINISHED Message: Padding Invalid\n"));
                SP_RETURN(SP_LOG_RESULT(PCT_INT_MSG_ALTERED));
            }
        }
        Encrypted.cbData -= cbPadding;
    }


    if(Encrypted.cbData < pContext->pReadHashInfo->cbCheckSum)
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_MSG_ALTERED));
    }

    // Validate MAC.
    Clean.pvBuffer = Encrypted.pvBuffer;
    Clean.cbData   = Encrypted.cbData - pContext->pReadHashInfo->cbCheckSum;
    Clean.cbBuffer = Clean.cbData;

    if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3)
    {
        pctRet = Ssl3ComputeMac(pContext,
                                TRUE,
                                &Clean,
                                pswrap->bCType,
                                rgbDigest,
                                sizeof(rgbDigest));
        if(pctRet != PCT_ERR_OK)
        {
            return pctRet;
        }
    }
    else
    {
        pctRet = Tls1ComputeMac(pContext,
                                TRUE,
                                &Clean,
                                pswrap->bCType,
                                rgbDigest,
                                sizeof(rgbDigest));
        if(pctRet != PCT_ERR_OK)
        {
            return pctRet;
        }
    }

    pContext->ReadCounter++;

    pbMAC = (PUCHAR)Clean.pvBuffer + Clean.cbData;

    if(memcmp(rgbDigest, pbMAC, pContext->pReadHashInfo->cbCheckSum))
    {
        DebugLog((DEB_WARN, "FINISHED Message: Checksum Invalid\n"));
        if(pContext->RipeZombie->fProtocol & SP_PROT_TLS1)
        {
            SetTls1Alert(pContext, TLS1_ALERT_FATAL, TLS1_ALERT_BAD_RECORD_MAC);
        }
        SP_RETURN(SP_LOG_RESULT(SEC_E_MESSAGE_ALTERED));
    }

    if(pAppOutput->pvBuffer != Clean.pvBuffer)
    {
        CopyMemory(pAppOutput->pvBuffer, Clean.pvBuffer, Clean.cbData);
    }

    pAppOutput->cbData = Clean.cbData;

    SP_RETURN(PCT_ERR_OK);
}


/*****************************************************************************/
// Create an encrypted Finish message, adding it to the end of the
// specified buffer object.
//
SP_STATUS SPBuildS3FinalFinish(PSPContext pContext, PSPBuffer pBuffer, BOOL fClient)
{
    PBYTE pbMessage = (PBYTE)pBuffer->pvBuffer + pBuffer->cbData;
    DWORD cbFinished;
    SP_STATUS pctRet;
    DWORD cbDataOut;

    BYTE rgbMd5Digest[CB_MD5_DIGEST_LEN];
    BYTE rgbSHADigest[CB_SHA_DIGEST_LEN];

    // Build Finished message body.
    pctRet = Ssl3BuildFinishMessage(pContext, rgbMd5Digest, rgbSHADigest, fClient);
    if(pctRet != PCT_ERR_OK)
    {
        return pctRet;
    }

    CopyMemory(pbMessage + sizeof(SWRAP) + sizeof(SHSH),
               rgbMd5Digest,
               CB_MD5_DIGEST_LEN);
    CopyMemory(pbMessage + sizeof(SWRAP) + sizeof(SHSH) + CB_MD5_DIGEST_LEN,
               rgbSHADigest,
               CB_SHA_DIGEST_LEN);

    // Build Finished handshake header.
    SetHandshake(pbMessage + sizeof(SWRAP),
                 SSL3_HS_FINISHED,
                 NULL,
                 CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN);
    cbFinished = sizeof(SHSH) + CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN;

    // Update handshake hash objects.
    pctRet = UpdateHandshakeHash(pContext,
                                 pbMessage + sizeof(SWRAP),
                                 cbFinished,
                                 FALSE);
    if(pctRet != PCT_ERR_OK)
    {
        return(pctRet);
    }

    // Add record header and encrypt message.
    pctRet = SPSetWrap(pContext,
            pbMessage,
            SSL3_CT_HANDSHAKE,
            cbFinished,
            fClient,
            &cbDataOut);

    if(pctRet != PCT_ERR_OK)
    {
        return pctRet;
    }

    // Update buffer length.
    pBuffer->cbData += cbDataOut;

    SP_ASSERT(pBuffer->cbData <= pBuffer->cbBuffer);

    return PCT_ERR_OK;
}

SP_STATUS
SPSetWrap(
    PSPContext pContext,
    PUCHAR pbMessage,
    UCHAR bContentType,
    DWORD cbPayload,
    BOOL fClient,
    DWORD *pcbDataOut)
{
    SWRAP *pswrap = (SWRAP *)pbMessage;
    DWORD cbMessage;
    SP_STATUS pctRet = PCT_ERR_OK;

    // Compute size of encrypted message.
    cbMessage = Ssl3CiphertextLen(pContext, cbPayload, fClient);

    if(pContext->pWriteHashInfo)
    {
        SPBuffer Clean;
        SPBuffer Encrypted;

        Clean.pvBuffer = pbMessage + sizeof(SWRAP);
        Clean.cbBuffer = cbMessage;
        Clean.cbData   = cbPayload;

        Encrypted.pvBuffer  = pbMessage;
        Encrypted.cbBuffer  = cbMessage;
        Encrypted.cbData    = cbPayload + sizeof(SWRAP);

        pctRet = Ssl3EncryptRaw(pContext, &Clean, &Encrypted, bContentType);
        cbMessage = Encrypted.cbData;
    }

    ZeroMemory(pswrap, sizeof(SWRAP));
    pswrap->bCType      = bContentType;
    pswrap->bMajor      = SSL3_CLIENT_VERSION_MSB;
    if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3)
    {
        pswrap->bMinor = (UCHAR)SSL3_CLIENT_VERSION_LSB;
    }
    else
    {
        pswrap->bMinor = (UCHAR)TLS1_CLIENT_VERSION_LSB;
    }
    pswrap->bcbMSBSize  = MSBOF(cbMessage - sizeof(SWRAP));
    pswrap->bcbLSBSize  = LSBOF(cbMessage - sizeof(SWRAP));

    if(pcbDataOut != NULL)
    {
        *pcbDataOut = cbMessage;
    }

    return(pctRet);
}

void
SetWrapNoEncrypt(
    PUCHAR pbMessage,
    UCHAR bContentType,
    DWORD cbPayload)
{
    SWRAP *pswrap = (SWRAP *)pbMessage;

    ZeroMemory(pswrap, sizeof(SWRAP));
    pswrap->bCType      = bContentType;
    pswrap->bMajor      = SSL3_CLIENT_VERSION_MSB;
    pswrap->bMinor      = SSL3_CLIENT_VERSION_LSB;
    pswrap->bcbMSBSize  = MSBOF(cbPayload);
    pswrap->bcbLSBSize  = LSBOF(cbPayload);
}


void SetHandshake(PUCHAR pb, BYTE bHandshake, PUCHAR pbData, DWORD dwSize)
{
    SHSH *pshsh = (SHSH *) pb;

    FillMemory(pshsh, sizeof(SHSH), 0);
    pshsh->typHS = bHandshake;
    pshsh->bcbMSB = MSBOF(dwSize) ;
    pshsh->bcbLSB = LSBOF(dwSize) ;
    if(NULL != pbData)
    {
        CopyMemory( pb + sizeof(SHSH) , pbData, dwSize);
    }
}



//+---------------------------------------------------------------------------
//
//  Function:   UpdateHandshakeHash
//
//  Synopsis:
//
//  Arguments:  [pContext]      --
//              [pb]            --
//              [dwcb]          --
//              [fInit]         --
//
//  History:    10-03-97   jbanes   Added server-side CAPI integration.
//
//  Notes:
//
//----------------------------------------------------------------------------
SP_STATUS
UpdateHandshakeHash(
    PSPContext  pContext,
    PUCHAR      pb,
    DWORD       dwcb,
    BOOL        fInit)
{
    if(pContext->RipeZombie->hMasterProv == 0)
    {
        return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
    }

    if(fInit)
    {
        DebugLog((DEB_TRACE, "UpdateHandshakeHash: initializing\n"));

        if(pContext->hMd5Handshake)
        {
            SchCryptDestroyHash(pContext->hMd5Handshake,
                                pContext->RipeZombie->dwCapiFlags);
            pContext->hMd5Handshake = 0;
        }
        if(!SchCryptCreateHash(pContext->RipeZombie->hMasterProv,
                               CALG_MD5, 0, 0,
                               &pContext->hMd5Handshake,
                               pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            return PCT_INT_INTERNAL_ERROR;
        }

        if(pContext->hShaHandshake)
        {
            SchCryptDestroyHash(pContext->hShaHandshake,
                                pContext->RipeZombie->dwCapiFlags);
            pContext->hShaHandshake = 0;
        }
        if(!SchCryptCreateHash(pContext->RipeZombie->hMasterProv,
                               CALG_SHA, 0, 0,
                               &pContext->hShaHandshake,
                               pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            return PCT_INT_INTERNAL_ERROR;
        }
    }

    if(pContext->hMd5Handshake == 0 || pContext->hShaHandshake == 0)
    {
        return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
    }

    if(dwcb && NULL != pb)
    {
        DebugLog((DEB_TRACE, "UpdateHandshakeHash: %d bytes\n", dwcb));

        if(!SchCryptHashData(pContext->hMd5Handshake,
                             pb, dwcb, 0,
                             pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            return PCT_INT_INTERNAL_ERROR;
        }
        if(!SchCryptHashData(pContext->hShaHandshake,
                             pb, dwcb, 0,
                             pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            return PCT_INT_INTERNAL_ERROR;
        }
    }

    #if VERIFYHASH
        CopyMemory(&rgbF[ibF], pb, dwcb);
        ibF += dwcb;
    #endif

    return PCT_ERR_OK;
}


//+---------------------------------------------------------------------------
//
//  Function:   Tls1ComputeCertVerifyHashes
//
//  Synopsis:   Compute the hashes contained by a TLS
//              CertificateVerify message.
//
//  Arguments:  [pContext]  --  Schannel context.
//              [pbHash]    --
//              [cbHash]    --
//
//  History:    10-14-97   jbanes   Created.
//
//  Notes:      The data generated by this routine is always 36 bytes in
//              length, and consists of an MD5 hash followed by an SHA
//              hash.
//
//              The hash values are computed as:
//
//                  md5_hash = MD5(handshake_messages);
//
//                  sha_hash = SHA(handshake_messages);
//
//----------------------------------------------------------------------------
SP_STATUS
Tls1ComputeCertVerifyHashes(
    PSPContext  pContext,   // in
    PBYTE       pbMD5,      // out
    PBYTE       pbSHA)      // out
{
    HCRYPTHASH hHash = 0;
    DWORD cbData;

    if((pContext == NULL) ||
       (pContext->RipeZombie == NULL))
    {
        return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
    }

    if(pbMD5 != NULL)
    {
        // md5_hash = MD5(handshake_messages);
        if(!SchCryptDuplicateHash(pContext->hMd5Handshake,
                                  NULL,
                                  0,
                                  &hHash,
                                  pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            return PCT_INT_INTERNAL_ERROR;
        }
        cbData = CB_MD5_DIGEST_LEN;
        if(!SchCryptGetHashParam(hHash,
                                 HP_HASHVAL,
                                 pbMD5,
                                 &cbData,
                                 0,
                                 pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
            return PCT_INT_INTERNAL_ERROR;
        }
        SP_ASSERT(cbData == CB_MD5_DIGEST_LEN);
        if(!SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
        }
    }

    if(pbSHA != NULL)
    {
        // sha_hash = SHA(handshake_messages);
        if(!SchCryptDuplicateHash(pContext->hShaHandshake,
                                  NULL,
                                  0,
                                  &hHash,
                                  pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            return PCT_INT_INTERNAL_ERROR;
        }
        cbData = CB_SHA_DIGEST_LEN;
        if(!SchCryptGetHashParam(hHash,
                                 HP_HASHVAL, pbSHA,
                                 &cbData,
                                 0,
                                 pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
            return PCT_INT_INTERNAL_ERROR;
        }
        SP_ASSERT(cbData == CB_SHA_DIGEST_LEN);
        if(!SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
        }
    }

    return PCT_ERR_OK;
}

//+---------------------------------------------------------------------------
//
//  Function:   Ssl3ComputeCertVerifyHashes
//
//  Synopsis:   Compute the hashes contained by an SSL3
//              CertificateVerify message.
//
//  Arguments:  [pContext]  --  Schannel context.
//              [pbHash]    --
//              [cbHash]    --
//
//  History:    10-14-97   jbanes   Added CAPI integration.
//
//  Notes:      The data generated by this routine is always 36 bytes in
//              length, and consists of an MD5 hash followed by an SHA
//              hash.
//
//              The hash values are computed as follows:
//
//                  md5_hash = MD5(master_secret + pad2 +
//                                 MD5(handshake_messages + master_secret +
//                                     pad1));
//
//                  sha_hash = SHA(master_secret + pad2 +
//                                 SHA(handshake_messages + master_secret +
//                                     pad1));
//
//----------------------------------------------------------------------------
SP_STATUS
Ssl3ComputeCertVerifyHashes(
    PSPContext  pContext,   // in
    PBYTE       pbMD5,      // out
    PBYTE       pbSHA)      // out
{
    BYTE rgbPad1[CB_SSL3_MAX_MAC_PAD];
    BYTE rgbPad2[CB_SSL3_MAX_MAC_PAD];
    HCRYPTHASH hHash = 0;
    DWORD cbData;
    SP_STATUS pctRet;

    FillMemory(rgbPad1, sizeof(rgbPad1), PAD1_CONSTANT);
    FillMemory(rgbPad2, sizeof(rgbPad2), PAD2_CONSTANT);

    if(pbMD5 != NULL)
    {
        //
        // CertificateVerify.signature.md5_hash = MD5(master_secret + pad2 +
        //    MD5(handshake_messages + master_secret + pad1));
        //

        // Compute inner hash.
        if(!SchCryptDuplicateHash(pContext->hMd5Handshake,
                                  NULL,
                                  0,
                                  &hHash,
                                  pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        if(!SchCryptHashSessionKey(hHash,
                                   pContext->RipeZombie->hMasterKey,
                                   CRYPT_LITTLE_ENDIAN,
                                   pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        if(!SchCryptHashData(hHash,
                             rgbPad1,
                             CB_SSL3_MD5_MAC_PAD,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        cbData = CB_MD5_DIGEST_LEN;
        if(!SchCryptGetHashParam(hHash,
                                 HP_HASHVAL,
                                 pbMD5,
                                 &cbData,
                                 0,
                                 pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        SP_ASSERT(cbData == CB_MD5_DIGEST_LEN);
        if(!SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
        }
        hHash = 0;

        // Compute outer hash.
        if(!SchCryptCreateHash(pContext->RipeZombie->hMasterProv,
                               CALG_MD5,
                               0,
                               0,
                               &hHash,
                               pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        if(!SchCryptHashSessionKey(hHash,
                                   pContext->RipeZombie->hMasterKey,
                                   CRYPT_LITTLE_ENDIAN,
                                   pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        if(!SchCryptHashData(hHash,
                             rgbPad2,
                             CB_SSL3_MD5_MAC_PAD,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        if(!SchCryptHashData(hHash,
                             pbMD5,
                             CB_MD5_DIGEST_LEN,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        cbData = CB_MD5_DIGEST_LEN;
        if(!SchCryptGetHashParam(hHash,
                                 HP_HASHVAL,
                                 pbMD5,
                                 &cbData,
                                 0,
                                 pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        SP_ASSERT(cbData == CB_MD5_DIGEST_LEN);
        if(!SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
        }
        hHash = 0;
    }

    if(pbSHA != NULL)
    {
        //
        // CertificateVerify.signature.sha_hash = SHA(master_secret + pad2 +
        //    SHA(handshake_messages + master_secret + pad1));
        //

        // Compute inner hash.
        if(!SchCryptDuplicateHash(pContext->hShaHandshake,
                                  NULL,
                                  0,
                                  &hHash,
                                  pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        if(!SchCryptHashSessionKey(hHash,
                                   pContext->RipeZombie->hMasterKey,
                                   CRYPT_LITTLE_ENDIAN,
                                   pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        if(!SchCryptHashData(hHash,
                             rgbPad1,
                             CB_SSL3_SHA_MAC_PAD,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        cbData = CB_SHA_DIGEST_LEN;
        if(!SchCryptGetHashParam(hHash,
                                 HP_HASHVAL, 
                                 pbSHA,
                                 &cbData,
                                 0,
                                 pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        SP_ASSERT(cbData == CB_SHA_DIGEST_LEN);
        if(!SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
        }
        hHash = 0;

        // Compute outer hash.
        if(!SchCryptCreateHash(pContext->RipeZombie->hMasterProv,
                               CALG_SHA,
                               0,
                               0,
                               &hHash,
                               pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        if(!SchCryptHashSessionKey(hHash,
                                   pContext->RipeZombie->hMasterKey,
                                   CRYPT_LITTLE_ENDIAN,
                                   pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        if(!SchCryptHashData(hHash,
                             rgbPad2,
                             CB_SSL3_SHA_MAC_PAD,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        if(!SchCryptHashData(hHash,
                             pbSHA,
                             CB_SHA_DIGEST_LEN,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        cbData = CB_SHA_DIGEST_LEN;
        if(!SchCryptGetHashParam(hHash,
                                 HP_HASHVAL,
                                 pbSHA,
                                 &cbData,
                                 0,
                                 pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
            pctRet = PCT_INT_INTERNAL_ERROR;
            goto cleanup;
        }
        SP_ASSERT(cbData == CB_SHA_DIGEST_LEN);
        if(!SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
        }
        hHash = 0;
    }

    pctRet = PCT_ERR_OK;

cleanup:

    if(hHash)
    {
        SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
    }

    return pctRet;
}


SP_STATUS Ssl3HandleCCS(PSPContext pContext,
                   PUCHAR pb,
                   DWORD cbMessage)
{

    SP_STATUS pctRet = PCT_ERR_OK;
    BOOL fSender =
        (0 == (pContext->RipeZombie->fProtocol & SP_PROT_SSL3TLS1_CLIENTS)) ;


    SP_BEGIN("Ssl3HandleCCS");

    if(cbMessage != 1 || pb[0] != 0x1)
    {
        pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
        SP_RETURN(pctRet);
    }

    // We always zero out the read counter on receipt
    // of a change cipher spec message.
    pContext->ReadCounter = 0;


    // Move pending ciphers to real ciphers
    pctRet = ContextInitCiphers(pContext, TRUE, FALSE);

    if(pctRet != PCT_ERR_OK)
    {
        SP_RETURN(pctRet);
    }

    if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3)
    {
        pctRet = Ssl3MakeReadSessionKeys(pContext);
    }
    else
    {
        pctRet = Tls1MakeReadSessionKeys(pContext);
    }
    if(pctRet != PCT_ERR_OK)
    {
        SP_RETURN(pctRet);
    }

    if(fSender)
    {
        pContext->wS3CipherSuiteClient = (WORD)UniAvailableCiphers[pContext->dwPendingCipherSuiteIndex].CipherKind;
        pContext->State = SSL3_STATE_CHANGE_CIPHER_SPEC_SERVER;
    }
    else
    {
        pContext->wS3CipherSuiteServer = (WORD)UniAvailableCiphers[pContext->dwPendingCipherSuiteIndex].CipherKind;
        pContext->State = SSL3_STATE_CHANGE_CIPHER_SPEC_CLIENT;
    }
    SP_RETURN(PCT_ERR_OK);
}


/*****************************************************************************/
// Create a (possibly encrypted) ChangeCipherSpec and an encrypted
// Finish message, adding them to the end of the specified buffer object.
//
SP_STATUS
BuildCCSAndFinishMessage(
    PSPContext pContext,
    PSPBuffer pBuffer,
    BOOL fClient)
{
    SP_STATUS pctRet;
    PBYTE pbMessage = (PBYTE)pBuffer->pvBuffer + pBuffer->cbData;
    DWORD cbDataOut;

    // Build ChangeCipherSpec message body.
    *(pbMessage + sizeof(SWRAP)) = 0x1;

    // Add record header and encrypt message.
    pctRet = SPSetWrap(pContext,
            pbMessage,
            SSL3_CT_CHANGE_CIPHER_SPEC,
            1,
            fClient,
            &cbDataOut);

    if(pctRet != PCT_ERR_OK)
        return(pctRet);

    // Update buffer length.
    pBuffer->cbData += cbDataOut;

    SP_ASSERT(pBuffer->cbData <= pBuffer->cbBuffer);

    // Update cipher suites.
    pContext->WriteCounter = 0;

    pctRet = ContextInitCiphers(pContext, FALSE, TRUE);
    if(pctRet != PCT_ERR_OK)
    {
        return(pctRet);
    }

    if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3)
    {
        pctRet = Ssl3MakeWriteSessionKeys(pContext);
    }
    else
    {
        pctRet = Tls1MakeWriteSessionKeys(pContext);
    }
    if(pctRet != PCT_ERR_OK)
    {
        return(pctRet);
    }

    if(fClient)
    {
        pContext->wS3CipherSuiteClient = (WORD)UniAvailableCiphers[pContext->dwPendingCipherSuiteIndex].CipherKind;
    }
    else
    {
        pContext->wS3CipherSuiteServer = (WORD)UniAvailableCiphers[pContext->dwPendingCipherSuiteIndex].CipherKind;
    }

    // Build Finish message.
    if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3)
    {
        pctRet = SPBuildS3FinalFinish(pContext, pBuffer, fClient);
    }
    else
    {
        pctRet = SPBuildTls1FinalFinish(pContext, pBuffer, fClient);
    }

    return pctRet;
}



SP_STATUS
Ssl3SelectCipher
(
    PSPContext pContext,
    WORD       wCipher
)
{
    SP_STATUS          pctRet=PCT_ERR_ILLEGAL_MESSAGE;
    DWORD               i;
    PCipherInfo         pCipherInfo = NULL;
    PHashInfo           pHashInfo = NULL;
    PKeyExchangeInfo    pExchInfo = NULL;

    pContext->dwPendingCipherSuiteIndex = 0;

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

        // Is this the right cipher suite?
        if(UniAvailableCiphers[i].CipherKind != wCipher)
        {
            continue;
        }

        pCipherInfo = GetCipherInfo(UniAvailableCiphers[i].aiCipher, UniAvailableCiphers[i].dwStrength);
        pHashInfo = GetHashInfo(UniAvailableCiphers[i].aiHash);
        pExchInfo = GetKeyExchangeInfo(UniAvailableCiphers[i].KeyExch);

        if(!IsCipherAllowed(pContext,
                            pCipherInfo,
                            pContext->RipeZombie->fProtocol,
                            pContext->RipeZombie->dwCF))
        {
            continue;
        }
        if(!IsHashAllowed(pContext, pHashInfo, pContext->RipeZombie->fProtocol))
        {
            continue;
        }
        if(!IsExchAllowed(pContext, pExchInfo, pContext->RipeZombie->fProtocol))
        {
            continue;
        }


        if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3TLS1_SERVERS)
        {
            // Determine the credentials (and CSP) to use, based on the
            // key exchange algorithm.
            pctRet = SPPickClientCertificate(pContext,
                                             UniAvailableCiphers[i].KeyExch);

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

        pContext->RipeZombie->dwCipherSuiteIndex = i;
        pContext->RipeZombie->aiCipher  = UniAvailableCiphers[i].aiCipher;
        pContext->RipeZombie->dwStrength  = UniAvailableCiphers[i].dwStrength;
        pContext->RipeZombie->aiHash  = UniAvailableCiphers[i].aiHash;
        pContext->RipeZombie->SessExchSpec  = UniAvailableCiphers[i].KeyExch;

        return ContextInitCiphersFromCache(pContext);
    }

    return(SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE));
}

// Server side cipher selection

SP_STATUS
Ssl3SelectCipherEx(
    PSPContext pContext,
    DWORD *pCipherSpecs,
    DWORD cCipherSpecs)
{
    DWORD i, j;
    SP_STATUS pctRet;
    PCipherInfo         pCipherInfo = NULL;
    PHashInfo           pHashInfo = NULL;
    PKeyExchangeInfo    pExchInfo = NULL;
    PSPCredential       pCred = NULL;
    BOOL                fFound;

    pContext->dwPendingCipherSuiteIndex = 0;

    // Loop through the supported SSL3 cipher suites.
    for(i = 0; i < UniNumCiphers; i++)
    {
        // Is this an SSL3 cipher suite?
        if(!(UniAvailableCiphers[i].fProt & pContext->RipeZombie->fProtocol))
        {
            continue;
        }

        pCipherInfo = GetCipherInfo(UniAvailableCiphers[i].aiCipher,
                                    UniAvailableCiphers[i].dwStrength);
        pHashInfo = GetHashInfo(UniAvailableCiphers[i].aiHash);
        pExchInfo = GetKeyExchangeInfo(UniAvailableCiphers[i].KeyExch);

        // Do we currently support this hash and key exchange algorithm?
        if(!IsHashAllowed(pContext, pHashInfo, pContext->RipeZombie->fProtocol))
        {
            DebugLog((DEB_TRACE, "Cipher %d - hash not supported\n", i));
            continue;
        }
        if(!IsExchAllowed(pContext, pExchInfo, pContext->RipeZombie->fProtocol))
        {
            DebugLog((DEB_TRACE, "Cipher %d - exch not supported\n", i));
            continue;
        }

        // Do we have an appropriate certificate?
        if(pContext->RipeZombie->fProtocol & SP_PROT_SSL3TLS1_SERVERS)
        {
            pctRet = SPPickServerCertificate(pContext,
                                             UniAvailableCiphers[i].KeyExch);

            if(pctRet != PCT_ERR_OK)
            {
                DebugLog((DEB_TRACE, "Cipher %d - certificate %d not found\n",
                    i, UniAvailableCiphers[i].KeyExch));
                continue;
            }
        }
        pCred = pContext->RipeZombie->pActiveServerCred;


        // Do we support this encryption algorithm/key length?
        if(!IsCipherSuiteAllowed(pContext,
                            pCipherInfo,
                            pContext->RipeZombie->fProtocol,
                            pCred->dwCF,
                            UniAvailableCiphers[i].dwFlags))
        {
            DebugLog((DEB_TRACE, "Cipher %d - cipher not supported\n", i));
            continue;
        }

        // Is this cipher suite supported by the client?
        for(fFound = FALSE, j = 0; j < cCipherSpecs; j++)
        {
            if(UniAvailableCiphers[i].CipherKind == pCipherSpecs[j])
            {
                fFound = TRUE;
                break;
            }
        }
        if(!fFound)
        {
            DebugLog((DEB_TRACE, "Cipher %d - not supported by client\n", i));
            continue;
        }


        if(UniAvailableCiphers[i].KeyExch == SP_EXCH_RSA_PKCS1)
        {
            // This is an RSA cipher suite, so make sure that the
            // CSP supports it.
            if(!IsAlgSupportedCapi(pContext->RipeZombie->fProtocol,
                                   UniAvailableCiphers + i,
                                   pCred->pCapiAlgs,
                                   pCred->cCapiAlgs))
            {
                DebugLog((DEB_TRACE, "Cipher %d - not supported by csp\n", i));
                continue;
            }
        }


        if(UniAvailableCiphers[i].KeyExch == SP_EXCH_DH_PKCS3)
        {
            // This is a DH cipher suite, so make sure that the
            // CSP supports it.
            if(!IsAlgSupportedCapi(pContext->RipeZombie->fProtocol,
                                   UniAvailableCiphers + i,
                                   pCred->pCapiAlgs,
                                   pCred->cCapiAlgs))
            {
                DebugLog((DEB_TRACE, "Cipher %d - not supported by csp\n", i));
                continue;
            }
        }


        // Use this cipher.
        pContext->RipeZombie->dwCipherSuiteIndex = i;
        pContext->RipeZombie->aiCipher      = UniAvailableCiphers[i].aiCipher;
        pContext->RipeZombie->dwStrength    = UniAvailableCiphers[i].dwStrength;
        pContext->RipeZombie->aiHash        = UniAvailableCiphers[i].aiHash;
        pContext->RipeZombie->SessExchSpec  = UniAvailableCiphers[i].KeyExch;
        pContext->RipeZombie->dwCF          = pCred->dwCF;

        return ContextInitCiphersFromCache(pContext);
    }

    LogCipherMismatchEvent();

    return SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
}


/*****************************************************************************/
VOID ComputeServerExchangeHashes(
    PSPContext pContext,
    PBYTE pbServerParams,      // in
    INT   iServerParamsLen,    // in
    PBYTE pbMd5HashVal,        // out
    PBYTE pbShaHashVal)        // out
{
    MD5_CTX Md5Hash;
    A_SHA_CTX ShaHash;

    //
    // md5_hash = MD5(ClientHello.random + ServerHello.random + ServerParams);
    //
    // sha_hash = SHA(ClientHello.random + ServerHello.random + ServerParams);
    //

    MD5Init(&Md5Hash);
    MD5Update(&Md5Hash, pContext->rgbS3CRandom, 32);
    MD5Update(&Md5Hash, pContext->rgbS3SRandom, 32);
    MD5Update(&Md5Hash, pbServerParams, iServerParamsLen);
    MD5Final(&Md5Hash);
    CopyMemory(pbMd5HashVal, Md5Hash.digest, 16);

    A_SHAInit(&ShaHash);
    A_SHAUpdate(&ShaHash, pContext->rgbS3CRandom, 32);
    A_SHAUpdate(&ShaHash, pContext->rgbS3SRandom, 32);
    A_SHAUpdate(&ShaHash, pbServerParams, iServerParamsLen);
    A_SHAFinal(&ShaHash, pbShaHashVal);
}

SP_STATUS
UnwrapSsl3Message(
    PSPContext pContext,
    PSPBuffer pMsgInput)
{
    SPBuffer   Encrypted;
    SPBuffer   Clean;
    SP_STATUS pctRet;
    SWRAP *pswrap = (SWRAP *)pMsgInput->pvBuffer;
    PBYTE pbMsg = (PBYTE)pMsgInput->pvBuffer;

    //
    // Validate 5 byte header.
    //

    // ProtocolVersion version;
    if(COMBINEBYTES(pbMsg[1], pbMsg[2])  < SSL3_CLIENT_VERSION)
    {
        pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    if(COMBINEBYTES(pswrap->bcbMSBSize, pswrap->bcbLSBSize) <
                        pContext->pReadHashInfo->cbCheckSum)
    {
        return(PCT_ERR_ILLEGAL_MESSAGE);
    }

    Encrypted.pvBuffer = pMsgInput->pvBuffer;
    Encrypted.cbBuffer = pMsgInput->cbBuffer;
    Encrypted.cbData = pMsgInput->cbData;
    Clean.pvBuffer = (PUCHAR)pMsgInput->pvBuffer + sizeof(SWRAP);
    pctRet = Ssl3DecryptMessage(pContext, &Encrypted, &Clean);
    if(pctRet == PCT_ERR_OK)
    {
        pswrap->bcbMSBSize = MSBOF(Clean.cbData);
        pswrap->bcbLSBSize = LSBOF(Clean.cbData);
    }
    return(pctRet);
}



SP_STATUS
ParseAlertMessage(
    PSPContext pContext,
    PUCHAR pbAlertMsg,
    DWORD cbMessage
    )
{
    SP_STATUS   pctRet=PCT_ERR_OK;
    if(cbMessage != 2)
    {
        return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    if(pbAlertMsg[0] != SSL3_ALERT_WARNING  &&  pbAlertMsg[0] != SSL3_ALERT_FATAL)
    {
        return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    DebugLog((DEB_WARN, "AlertMessage, Alert Level -  %lx\n", (DWORD)pbAlertMsg[0]));
    DebugLog((DEB_WARN, "AlertMessage, Alert Description -  %lx\n", (DWORD)pbAlertMsg[1]));

    if(pbAlertMsg[0] == SSL3_ALERT_WARNING)
    {
        switch(pbAlertMsg[1])
        {
        case SSL3_ALERT_NO_CERTIFICATE:
            DebugLog((DEB_TRACE, "no_certificate alert\n"));
            pContext->State = SSL3_STATE_NO_CERT_ALERT;
            pctRet = PCT_ERR_OK;
            break;

        case SSL3_ALERT_CLOSE_NOTIFY:
            DebugLog((DEB_TRACE, "close_notify alert\n"));
            pctRet = SEC_I_CONTEXT_EXPIRED;
            break;

        default:
            DebugLog((DEB_TRACE, "Ignoring warning alert\n"));
            pctRet = PCT_ERR_OK;
            break;
        }
    }
    else
    {
        switch(pbAlertMsg[1])
        {
        case SSL3_ALERT_UNEXPECTED_MESSAGE:
            DebugLog((DEB_TRACE, "unexpected_message alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_ILLEGAL_MESSAGE);
            break;

        case TLS1_ALERT_BAD_RECORD_MAC:
            DebugLog((DEB_TRACE, "bad_record_mac alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_MESSAGE_ALTERED);
            break;

        case TLS1_ALERT_DECRYPTION_FAILED:
            DebugLog((DEB_TRACE, "decryption_failed alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_DECRYPT_FAILURE);
            break;

        case TLS1_ALERT_RECORD_OVERFLOW:
            DebugLog((DEB_TRACE, "record_overflow alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_ILLEGAL_MESSAGE);
            break;

        case SSL3_ALERT_DECOMPRESSION_FAIL:
            DebugLog((DEB_TRACE, "decompression_fail alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_MESSAGE_ALTERED);
            break;

        case SSL3_ALERT_HANDSHAKE_FAILURE:
            DebugLog((DEB_TRACE, "handshake_failure alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_ILLEGAL_MESSAGE);
            break;

        case TLS1_ALERT_BAD_CERTIFICATE:
            DebugLog((DEB_TRACE, "bad_certificate alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_CERT_UNKNOWN);
            break;

        case TLS1_ALERT_UNSUPPORTED_CERT:
            DebugLog((DEB_TRACE, "unsupported_cert alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_CERT_UNKNOWN);
            break;

        case TLS1_ALERT_CERTIFICATE_REVOKED:
            DebugLog((DEB_TRACE, "certificate_revoked alert\n"));
            pctRet = SP_LOG_RESULT(CRYPT_E_REVOKED);
            break;

        case TLS1_ALERT_CERTIFICATE_EXPIRED:
            DebugLog((DEB_TRACE, "certificate_expired alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_CERT_EXPIRED);
            break;

        case TLS1_ALERT_CERTIFICATE_UNKNOWN:
            DebugLog((DEB_TRACE, "certificate_unknown alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_CERT_UNKNOWN);
            break;

        case SSL3_ALERT_ILLEGAL_PARAMETER:
            DebugLog((DEB_TRACE, "illegal_parameter alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_ILLEGAL_MESSAGE);
            break;

        case TLS1_ALERT_UNKNOWN_CA:
            DebugLog((DEB_TRACE, "unknown_ca alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_UNTRUSTED_ROOT);
            break;

        case TLS1_ALERT_ACCESS_DENIED:
            DebugLog((DEB_TRACE, "access_denied alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_LOGON_DENIED);
            break;

        case TLS1_ALERT_DECODE_ERROR:
            DebugLog((DEB_TRACE, "decode_error alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_ILLEGAL_MESSAGE);
            break;

        case TLS1_ALERT_DECRYPT_ERROR:
            DebugLog((DEB_TRACE, "decrypt_error alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_DECRYPT_FAILURE);
            break;

        case TLS1_ALERT_EXPORT_RESTRICTION:
            DebugLog((DEB_TRACE, "export_restriction alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_ILLEGAL_MESSAGE);
            break;

        case TLS1_ALERT_PROTOCOL_VERSION:
            DebugLog((DEB_TRACE, "protocol_version alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_UNSUPPORTED_FUNCTION);
            break;

        case TLS1_ALERT_INSUFFIENT_SECURITY:
            DebugLog((DEB_TRACE, "insuffient_security alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_ALGORITHM_MISMATCH);
            break;

        case TLS1_ALERT_INTERNAL_ERROR:
            DebugLog((DEB_TRACE, "internal_error alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
            break;

        default:
            DebugLog((DEB_TRACE, "Unknown fatal alert\n"));
            pctRet = SP_LOG_RESULT(SEC_E_ILLEGAL_MESSAGE);
            break;
        }
    }

    return pctRet;
}


void BuildAlertMessage(PBYTE pbAlertMsg, UCHAR bAlertLevel, UCHAR bAlertDesc)
{
    ALRT *palrt = (ALRT *) pbAlertMsg;

    FillMemory(palrt, sizeof(ALRT), 0);

    palrt->bCType = SSL3_CT_ALERT;
    palrt->bMajor = SSL3_CLIENT_VERSION_MSB;
//  palrt->bMinor = SSL3_CLIENT_VERSION_LSB; DONE by FillMemory
//  palrt->bcbMSBSize = 0; Done by FillMemory
    palrt->bcbLSBSize = 2;
    palrt->bAlertLevel = bAlertLevel;
    palrt->bAlertDesc  = bAlertDesc ;
}


SP_STATUS SPPacketSplit(BYTE bContentType, PSPBuffer pPlain)
    //Now let's us see whether we have the FULL-handshake
    {
        SP_STATUS pctRet = PCT_ERR_OK;
        PBYTE pb;
        DWORD cb;

        pb = pPlain->pvBuffer;

        switch(bContentType)
        {
        case SSL3_CT_HANDSHAKE:
            if(pPlain->cbData >= sizeof(SHSH))
            {
                cb = ((INT)pb[1] << 16) + ((INT)pb[2] << 8) + (INT)pb[3];
                cb += sizeof(SHSH);
                if( cb > pPlain->cbData)
                {
                    return(PCT_INT_INCOMPLETE_MSG);
                }

            }
            else
                return(PCT_INT_INCOMPLETE_MSG);
            break;
        case SSL3_CT_ALERT:
            if(pPlain->cbData != 2)
                return(PCT_INT_INCOMPLETE_MSG);
            break;
        case SSL3_CT_CHANGE_CIPHER_SPEC:
            if(pPlain->cbData != 1)
                return(PCT_INT_INTERNAL_ERROR);
        default:
            break;
        }

        return(pctRet);
    }



/*****************************************************************************/
// Create an encrypted Finish message, adding it to the end of the
// specified buffer object.
//
SP_STATUS SPBuildTls1FinalFinish(PSPContext pContext, PSPBuffer pBuffer, BOOL fClient)
{
    PBYTE pbMessage = (PBYTE)pBuffer->pvBuffer + pBuffer->cbData;
    DWORD cbFinished;
    SP_STATUS pctRet;
    DWORD cbDataOut;

    BYTE  rgbDigest[CB_TLS1_VERIFYDATA];

    // Build Finished message body.
    pctRet = Tls1BuildFinishMessage(pContext, rgbDigest, sizeof(rgbDigest), fClient);
    if(pctRet != PCT_ERR_OK)
    {
        return pctRet;
    }

    CopyMemory(pbMessage + sizeof(SWRAP) + sizeof(SHSH),
               rgbDigest,
               CB_TLS1_VERIFYDATA);

    // Build Finished handshake header.
    SetHandshake(pbMessage + sizeof(SWRAP),
                 SSL3_HS_FINISHED,
                 NULL,
                 CB_TLS1_VERIFYDATA);
    cbFinished = sizeof(SHSH) + CB_TLS1_VERIFYDATA;

    // Update handshake hash objects.
    pctRet = UpdateHandshakeHash(pContext,
                                 pbMessage + sizeof(SWRAP),
                                 cbFinished,
                                 FALSE);
    if(pctRet != PCT_ERR_OK)
    {
        return(pctRet);
    }

    // Add record header and encrypt message.
    pctRet = SPSetWrap(pContext,
            pbMessage,
            SSL3_CT_HANDSHAKE,
            cbFinished,
            fClient,
            &cbDataOut);

    if(pctRet != PCT_ERR_OK)
    {
        return(pctRet);
    }

    // Update buffer length .
    pBuffer->cbData += cbDataOut;

    SP_ASSERT(pBuffer->cbData <= pBuffer->cbBuffer);

    return pctRet;
}


//+---------------------------------------------------------------------------
//
//  Function:   Tls1BuildFinishMessage
//
//  Synopsis:   Compute a TLS MAC for the specified message.
//
//  Arguments:  [pContext]      --  Schannel context.
//              [pbVerifyData]  --  Verify data buffer.
//              [cbVerifyData]  --  Length of verify data buffer.
//              [fClient]       --  Client-generated Finished?
//
//  History:    10-13-97   jbanes   Created.
//
//  Notes:      The Finished message is computed using the following formula:
//
//              verify_data = PRF(master_secret, finished_label,
//                                MD5(handshake_messages) +
//                                SHA-1(handshake_messages)) [0..11];
//
//----------------------------------------------------------------------------
SP_STATUS
Tls1BuildFinishMessage(
    PSPContext  pContext,       // in
    PBYTE       pbVerifyData,   // out
    DWORD       cbVerifyData,   // in
    BOOL        fClient)        // in
{
    PBYTE pbLabel;
    DWORD cbLabel;
    UCHAR rgbData[CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN];
    DWORD cbData;
    HCRYPTHASH hHash = 0;
    CRYPT_DATA_BLOB Data;
    SP_STATUS pctRet;

    if(cbVerifyData < CB_TLS1_VERIFYDATA)
    {
        return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
    }

    if(fClient)
    {
        pbLabel = TLS1_LABEL_CLIENTFINISHED;
    }
    else
    {
        pbLabel = TLS1_LABEL_SERVERFINISHED;
    }
    cbLabel = CB_TLS1_LABEL_FINISHED;


    // Get the MD5 hash of the handshake messages so far.
    if(!SchCryptDuplicateHash(pContext->hMd5Handshake,
                              NULL,
                              0,
                              &hHash,
                              pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto error;
    }

    cbData = CB_MD5_DIGEST_LEN;
    if(!SchCryptGetHashParam(hHash,
                             HP_HASHVAL,
                             rgbData,
                             &cbData,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto error;
    }

    if(!SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
    }
    hHash = 0;

    // Get the SHA hash of the handshake messages so far.
    if(!SchCryptDuplicateHash(pContext->hShaHandshake,
                              NULL,
                              0,
                              &hHash,
                              pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto error;
    }

    cbData = A_SHA_DIGEST_LEN;
    if(!SchCryptGetHashParam(hHash,
                             HP_HASHVAL,
                             rgbData + CB_MD5_DIGEST_LEN,
                             &cbData,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto error;
    }

    cbData = CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN;

    if(!SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
    }
    hHash = 0;

    // Compute the PRF
    if(!SchCryptCreateHash(pContext->RipeZombie->hMasterProv,
                           CALG_TLS1PRF,
                           pContext->RipeZombie->hMasterKey,
                           0,
                           &hHash,
                           pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto error;
    }

    Data.pbData = pbLabel;
    Data.cbData = cbLabel;
    if(!SchCryptSetHashParam(hHash,
                             HP_TLS1PRF_LABEL,
                             (PBYTE)&Data,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto error;
    }

    Data.pbData = rgbData;
    Data.cbData = cbData;
    if(!SchCryptSetHashParam(hHash,
                             HP_TLS1PRF_SEED,
                             (PBYTE)&Data,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto error;
    }

    if(!SchCryptGetHashParam(hHash,
                             HP_HASHVAL,
                             pbVerifyData,
                             &cbVerifyData,
                             0,
                             pContext->RipeZombie->dwCapiFlags))
    {
        SP_LOG_RESULT(GetLastError());
        pctRet = PCT_INT_INTERNAL_ERROR;
        goto error;
    }

    pctRet = PCT_ERR_OK;


error:

    if(hHash)
    {
        if(!SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags))
        {
            SP_LOG_RESULT(GetLastError());
        }
    }

    return pctRet;
}

SP_STATUS
SPBuildTlsAlertMessage(
    PSPContext  pContext,       // in
    PSPBuffer pCommOutput)
{
    PBYTE pbMessage = NULL;
    DWORD cbMessage;
    BOOL  fAllocated = FALSE;
    SP_STATUS pctRet;
    DWORD cbDataOut;

    SP_BEGIN("SPBuildTlsAlertMessage");

    cbMessage =  sizeof(SWRAP) +
                         CB_SSL3_ALERT_ONLY +
                         SP_MAX_DIGEST_LEN +
                         SP_MAX_BLOCKCIPHER_SIZE;

    if(pContext->State != TLS1_STATE_ERROR)
    {
        SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
    }

    if(pCommOutput->pvBuffer)
    {
        // Application has allocated memory.
        if(pCommOutput->cbBuffer < cbMessage)
        {
            pCommOutput->cbData = cbMessage;
            return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
        }
        fAllocated = TRUE;
    }
    else
    {
        // Schannel is to allocate memory.
        pCommOutput->cbBuffer = cbMessage;
        pCommOutput->pvBuffer = SPExternalAlloc(cbMessage);
        if(pCommOutput->pvBuffer == NULL)
        {
            SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
        }
    }
    pCommOutput->cbData = 0;


    pbMessage = (PBYTE)pCommOutput->pvBuffer;


     // Build alert message.
    BuildAlertMessage(pbMessage,
                      pContext->bAlertLevel,
                      pContext->bAlertNumber);

#if DBG
    DBG_HEX_STRING(DEB_TRACE, pbMessage, sizeof(ALRT));
#endif

    // Build record header and encrypt message.
    pctRet = SPSetWrap(pContext,
                pbMessage,
                SSL3_CT_ALERT,
                CB_SSL3_ALERT_ONLY,
                pContext->dwProtocol & SP_PROT_SSL3TLS1_CLIENTS,
                &cbDataOut);

    if(pctRet !=  PCT_ERR_OK)
    {
        if(!fAllocated)
        {
            SPExternalFree(pCommOutput->pvBuffer);
            pCommOutput->pvBuffer = NULL;
        }
        SP_RETURN(SP_LOG_RESULT(pctRet));
    }

    // Update buffer length.
    pCommOutput->cbData = cbDataOut;

    SP_ASSERT(pCommOutput->cbData <= pCommOutput->cbBuffer);

    SP_RETURN(PCT_ERR_OK);
}


void
SetTls1Alert(
    PSPContext  pContext,
    BYTE        bAlertLevel,
    BYTE        bAlertNumber)
{
    pContext->State        = TLS1_STATE_ERROR;
    pContext->bAlertLevel  = bAlertLevel;
    pContext->bAlertNumber = bAlertNumber;
}