windows-nt/Source/XPSP1/NT/ds/security/protocols/schannel/spbase/ssl3msg.c

2810 lines
81 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// 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;
}