You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2746 lines
79 KiB
2746 lines
79 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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>
|
|
|
|
//------------------------------------------------------------------------------------------
|
|
|
|
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 either a HelloRequest message, which means that
|
|
// the server is requesting a renegotiation, or a ServerHello
|
|
// message, which means that the server is responding to a
|
|
// renegotiation request that we made.
|
|
|
|
// The first thing we need to do is to figure out which it is.
|
|
// We want to go ahead and decrypt the HelloRequest message, but
|
|
// if it's a server hello message we want to punt this back to the
|
|
// application so that they can give it to the LSA process via
|
|
// a call to InitializeSecurityContext.
|
|
|
|
// BUGBUG - We should change the context state when we request
|
|
// renegotiation and then just check the state here, but that's
|
|
// hard to do so for now we'll just check the size of the packet.
|
|
if(pCommInput->cbBuffer > sizeof(SWRAP) +
|
|
sizeof(SHSH) +
|
|
pContext->pCipherInfo->dwBlockSize +
|
|
pContext->pHashInfo->cbCheckSum)
|
|
{
|
|
// Must be a ServerHello message.
|
|
pCommInput->cbData = 0;
|
|
|
|
pContext->State = SSL3_STATE_CLIENT_HELLO;
|
|
}
|
|
else
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
pContext->State = SSL3_STATE_GEN_HELLO_REQUEST;
|
|
}
|
|
}
|
|
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;
|
|
pContext->State = SSL3_STATE_RENEGOTIATE;
|
|
}
|
|
|
|
pAppOutput->cbData = 0;
|
|
|
|
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)
|
|
{
|
|
UNREFERENCED_PARAMETER(pContext);
|
|
UNREFERENCED_PARAMETER(pCommInput);
|
|
|
|
if(pcbHeaderSize == NULL)
|
|
{
|
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
|
|
*pcbHeaderSize = sizeof(SWRAP);
|
|
return PCT_ERR_OK;
|
|
}
|
|
|
|
|
|
|
|
#if VERIFYHASH
|
|
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;
|
|
PHashInfo pHashInfo;
|
|
SP_STATUS pctRet;
|
|
|
|
UNREFERENCED_PARAMETER(cbMac);
|
|
|
|
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;
|
|
}
|
|
|
|
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(!CryptCreateHash(hProv,
|
|
pHashInfo->aiHash,
|
|
0,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
// Hash secret
|
|
if(!CryptHashSessionKey(hHash,
|
|
hSecret,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
// hash pad 1
|
|
FillMemory(rgbPad, cbPad, PAD1_CONSTANT);
|
|
if(!CryptHashData(hHash, rgbPad, cbPad, 0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* add count */
|
|
dwReverseSequence = 0;
|
|
if(!CryptHashData(hHash,
|
|
(PUCHAR)&dwReverseSequence,
|
|
sizeof(DWORD),
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
dwReverseSequence = htonl(dwSequence);
|
|
if(!CryptHashData(hHash,
|
|
(PUCHAR)&dwReverseSequence,
|
|
sizeof(DWORD),
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
// Add content type.
|
|
if(cContentType != 0)
|
|
{
|
|
if(!CryptHashData(hHash, (PBYTE)&cContentType, 1, 0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* add length */
|
|
wReverseData = (WORD)pClean->cbData >> 8 | (WORD)pClean->cbData << 8;
|
|
if(!CryptHashData(hHash,
|
|
(PBYTE)&wReverseData,
|
|
sizeof(WORD),
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* add data */
|
|
if(!CryptHashData(hHash,
|
|
pClean->pvBuffer,
|
|
pClean->cbData,
|
|
0))
|
|
{
|
|
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(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
rgbDigest,
|
|
&cbDigest,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
SP_ASSERT(pHashInfo->cbCheckSum == cbDigest);
|
|
|
|
CryptDestroyHash(hHash);
|
|
hHash = 0;
|
|
|
|
#if VERIFYHASH
|
|
CopyMemory(&rgb3Mac[ibMac], rgbDigest, pHashInfo->cbCheckSum);
|
|
ibMac += pHashInfo->cbCheckSum;
|
|
#endif
|
|
|
|
|
|
|
|
// Create hash
|
|
if(!CryptCreateHash(hProv,
|
|
pHashInfo->aiHash,
|
|
0,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
// Hash secret
|
|
if(!CryptHashSessionKey(hHash,
|
|
hSecret,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
// hash pad 2
|
|
FillMemory(rgbPad, cbPad, PAD2_CONSTANT);
|
|
if(!CryptHashData(hHash, rgbPad, cbPad, 0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(!CryptHashData(hHash, rgbDigest, cbDigest, 0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
// Get outer hash value.
|
|
cbDigest = sizeof(rgbDigest);
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
rgbDigest,
|
|
&cbDigest,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
SP_ASSERT(pHashInfo->cbCheckSum == cbDigest);
|
|
|
|
CryptDestroyHash(hHash);
|
|
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)
|
|
{
|
|
CryptDestroyHash(hHash);
|
|
}
|
|
|
|
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";
|
|
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(!CryptDuplicateHash(pContext->hMd5Handshake,
|
|
NULL,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
// Add rest of stuff to local MD5 hash object.
|
|
if(!CryptHashData(hHash,
|
|
fClient ? szClnt : szSrvr,
|
|
4,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(!CryptHashSessionKey(hHash,
|
|
pContext->RipeZombie->hMasterKey,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
rgbPad1,
|
|
CB_SSL3_MD5_MAC_PAD,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
cbDigest = CB_MD5_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbMd5Digest,
|
|
&cbDigest,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
CryptDestroyHash(hHash);
|
|
hHash = 0;
|
|
|
|
// Compute "parent" MD5 hash
|
|
if(!CryptCreateHash(pContext->RipeZombie->hMasterProv,
|
|
CALG_MD5,
|
|
0,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashSessionKey(hHash,
|
|
pContext->RipeZombie->hMasterKey,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
rgbPad2,
|
|
CB_SSL3_MD5_MAC_PAD,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
pbMd5Digest,
|
|
CB_MD5_DIGEST_LEN,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
cbDigest = CB_MD5_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbMd5Digest,
|
|
&cbDigest,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
CryptDestroyHash(hHash);
|
|
hHash = 0;
|
|
|
|
// Build SHA Hash
|
|
|
|
// Make local copy of the handshake_messages SHA hash object
|
|
if(!CryptDuplicateHash(pContext->hShaHandshake,
|
|
NULL,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
// SHA(handshake_messages + Sender + master_secret + pad1)
|
|
if(!CryptHashData(hHash,
|
|
fClient ? szClnt : szSrvr,
|
|
4,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashSessionKey(hHash,
|
|
pContext->RipeZombie->hMasterKey,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
rgbPad1,
|
|
CB_SSL3_SHA_MAC_PAD,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
cbDigest = A_SHA_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbSHADigest,
|
|
&cbDigest,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
CryptDestroyHash(hHash);
|
|
hHash = 0;
|
|
|
|
// SHA(master_secret + pad2 + SHA-hash);
|
|
if(!CryptCreateHash(pContext->RipeZombie->hMasterProv,
|
|
CALG_SHA,
|
|
0,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashSessionKey(hHash,
|
|
pContext->RipeZombie->hMasterKey,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
rgbPad2,
|
|
CB_SSL3_SHA_MAC_PAD,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
pbSHADigest,
|
|
A_SHA_DIGEST_LEN,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
cbDigest = A_SHA_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbSHADigest,
|
|
&cbDigest,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
CryptDestroyHash(hHash);
|
|
hHash = 0;
|
|
|
|
pctRet = PCT_ERR_OK;
|
|
|
|
cleanup:
|
|
|
|
if(hHash)
|
|
{
|
|
CryptDestroyHash(hHash);
|
|
}
|
|
|
|
return pctRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
DWORD Ssl3CiphertextLen(
|
|
PSPContext pContext,
|
|
DWORD cbMessage,
|
|
BOOL fClientIsSender)
|
|
{
|
|
DWORD cbBlock;
|
|
|
|
UNREFERENCED_PARAMETER(fClientIsSender);
|
|
|
|
// 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(!CryptEncrypt(pContext->hWriteKey,
|
|
0, FALSE, 0,
|
|
Encrypted.pvBuffer,
|
|
&Encrypted.cbData,
|
|
Encrypted.cbBuffer))
|
|
{
|
|
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;
|
|
BOOL fBadPadding = FALSE;
|
|
|
|
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);
|
|
|
|
cbBlock = pContext->pReadCipherInfo->dwBlockSize;
|
|
|
|
if(cbBlock > 1)
|
|
{
|
|
/* check to see if we have a block size violation */
|
|
if((Encrypted.cbData % cbBlock) || (Encrypted.cbData < cbBlock))
|
|
{
|
|
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(!CryptDecrypt(pContext->hReadKey,
|
|
0, FALSE, 0,
|
|
Encrypted.pvBuffer,
|
|
&Encrypted.cbData))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
SP_RETURN(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
}
|
|
|
|
// Remove block cipher padding.
|
|
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"));
|
|
fBadPadding = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(cbPadding > 256 || cbPadding >= Encrypted.cbData)
|
|
{
|
|
// Invalid pad size.
|
|
DebugLog((DEB_WARN, "FINISHED Message: Padding Invalid\n"));
|
|
fBadPadding = TRUE;
|
|
}
|
|
}
|
|
if(!fBadPadding)
|
|
{
|
|
Encrypted.cbData -= cbPadding;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Note that if the padding is bogus we don't error out right
|
|
// away. This might give an attacker some timing information with
|
|
// which he can attack the symmetric cipher (or CBC) independently
|
|
// of the MAC. Instead, we'll perform the MAC operation and then
|
|
// error out.
|
|
//
|
|
|
|
// Build buffer over which to MAC.
|
|
if(Encrypted.cbData < pContext->pReadHashInfo->cbCheckSum)
|
|
{
|
|
fBadPadding = TRUE;
|
|
|
|
Clean.pvBuffer = Encrypted.pvBuffer;
|
|
Clean.cbData = Encrypted.cbData;
|
|
Clean.cbBuffer = Clean.cbData;
|
|
}
|
|
else
|
|
{
|
|
Clean.pvBuffer = Encrypted.pvBuffer;
|
|
Clean.cbData = Encrypted.cbData - pContext->pReadHashInfo->cbCheckSum;
|
|
Clean.cbBuffer = Clean.cbData;
|
|
}
|
|
|
|
// Validate MAC.
|
|
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;
|
|
}
|
|
}
|
|
|
|
if(fBadPadding)
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(SEC_E_MESSAGE_ALTERED));
|
|
}
|
|
|
|
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)
|
|
{
|
|
CryptDestroyHash(pContext->hMd5Handshake);
|
|
pContext->hMd5Handshake = 0;
|
|
}
|
|
if(!CryptCreateHash(pContext->RipeZombie->hMasterProv,
|
|
CALG_MD5, 0, 0,
|
|
&pContext->hMd5Handshake))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
return PCT_INT_INTERNAL_ERROR;
|
|
}
|
|
|
|
if(pContext->hShaHandshake)
|
|
{
|
|
CryptDestroyHash(pContext->hShaHandshake);
|
|
pContext->hShaHandshake = 0;
|
|
}
|
|
if(!CryptCreateHash(pContext->RipeZombie->hMasterProv,
|
|
CALG_SHA, 0, 0,
|
|
&pContext->hShaHandshake))
|
|
{
|
|
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(!CryptHashData(pContext->hMd5Handshake,
|
|
pb, dwcb, 0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
return PCT_INT_INTERNAL_ERROR;
|
|
}
|
|
if(!CryptHashData(pContext->hShaHandshake,
|
|
pb, dwcb, 0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
return PCT_INT_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
|
|
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(!CryptDuplicateHash(pContext->hMd5Handshake,
|
|
NULL,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
return PCT_INT_INTERNAL_ERROR;
|
|
}
|
|
cbData = CB_MD5_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbMD5,
|
|
&cbData,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
CryptDestroyHash(hHash);
|
|
return PCT_INT_INTERNAL_ERROR;
|
|
}
|
|
SP_ASSERT(cbData == CB_MD5_DIGEST_LEN);
|
|
if(!CryptDestroyHash(hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
}
|
|
}
|
|
|
|
if(pbSHA != NULL)
|
|
{
|
|
// sha_hash = SHA(handshake_messages);
|
|
if(!CryptDuplicateHash(pContext->hShaHandshake,
|
|
NULL,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
return PCT_INT_INTERNAL_ERROR;
|
|
}
|
|
cbData = CB_SHA_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL, pbSHA,
|
|
&cbData,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
CryptDestroyHash(hHash);
|
|
return PCT_INT_INTERNAL_ERROR;
|
|
}
|
|
SP_ASSERT(cbData == CB_SHA_DIGEST_LEN);
|
|
if(!CryptDestroyHash(hHash))
|
|
{
|
|
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(!CryptDuplicateHash(pContext->hMd5Handshake,
|
|
NULL,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashSessionKey(hHash,
|
|
pContext->RipeZombie->hMasterKey,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
rgbPad1,
|
|
CB_SSL3_MD5_MAC_PAD,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
cbData = CB_MD5_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbMD5,
|
|
&cbData,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
SP_ASSERT(cbData == CB_MD5_DIGEST_LEN);
|
|
if(!CryptDestroyHash(hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
}
|
|
hHash = 0;
|
|
|
|
// Compute outer hash.
|
|
if(!CryptCreateHash(pContext->RipeZombie->hMasterProv,
|
|
CALG_MD5,
|
|
0,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashSessionKey(hHash,
|
|
pContext->RipeZombie->hMasterKey,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
rgbPad2,
|
|
CB_SSL3_MD5_MAC_PAD,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
pbMD5,
|
|
CB_MD5_DIGEST_LEN,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
cbData = CB_MD5_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbMD5,
|
|
&cbData,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
SP_ASSERT(cbData == CB_MD5_DIGEST_LEN);
|
|
if(!CryptDestroyHash(hHash))
|
|
{
|
|
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(!CryptDuplicateHash(pContext->hShaHandshake,
|
|
NULL,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashSessionKey(hHash,
|
|
pContext->RipeZombie->hMasterKey,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
rgbPad1,
|
|
CB_SSL3_SHA_MAC_PAD,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
cbData = CB_SHA_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbSHA,
|
|
&cbData,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
SP_ASSERT(cbData == CB_SHA_DIGEST_LEN);
|
|
if(!CryptDestroyHash(hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
}
|
|
hHash = 0;
|
|
|
|
// Compute outer hash.
|
|
if(!CryptCreateHash(pContext->RipeZombie->hMasterProv,
|
|
CALG_SHA, 0, 0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashSessionKey(hHash,
|
|
pContext->RipeZombie->hMasterKey,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
rgbPad2,
|
|
CB_SSL3_SHA_MAC_PAD,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if(!CryptHashData(hHash,
|
|
pbSHA,
|
|
CB_SHA_DIGEST_LEN,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
cbData = CB_SHA_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbSHA,
|
|
&cbData,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
SP_ASSERT(cbData == CB_SHA_DIGEST_LEN);
|
|
if(!CryptDestroyHash(hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
}
|
|
hHash = 0;
|
|
}
|
|
|
|
pctRet = PCT_ERR_OK;
|
|
|
|
cleanup:
|
|
|
|
if(hHash)
|
|
{
|
|
CryptDestroyHash(hHash);
|
|
}
|
|
|
|
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->dwCertFlags,
|
|
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->dwCertFlags;
|
|
|
|
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 ;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
// 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 = (PBYTE)TLS1_LABEL_CLIENTFINISHED;
|
|
}
|
|
else
|
|
{
|
|
pbLabel = (PBYTE)TLS1_LABEL_SERVERFINISHED;
|
|
}
|
|
cbLabel = CB_TLS1_LABEL_FINISHED;
|
|
|
|
|
|
// Get the MD5 hash of the handshake messages so far.
|
|
if(!CryptDuplicateHash(pContext->hMd5Handshake,
|
|
NULL,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
cbData = CB_MD5_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
rgbData,
|
|
&cbData,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
if(!CryptDestroyHash(hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
}
|
|
hHash = 0;
|
|
|
|
// Get the SHA hash of the handshake messages so far.
|
|
if(!CryptDuplicateHash(pContext->hShaHandshake,
|
|
NULL,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
cbData = A_SHA_DIGEST_LEN;
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
rgbData + CB_MD5_DIGEST_LEN,
|
|
&cbData,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
cbData = CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN;
|
|
|
|
if(!CryptDestroyHash(hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
}
|
|
hHash = 0;
|
|
|
|
// Compute the PRF
|
|
if(!CryptCreateHash(pContext->RipeZombie->hMasterProv,
|
|
CALG_TLS1PRF,
|
|
pContext->RipeZombie->hMasterKey,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
Data.pbData = pbLabel;
|
|
Data.cbData = cbLabel;
|
|
if(!CryptSetHashParam(hHash,
|
|
HP_TLS1PRF_LABEL,
|
|
(PBYTE)&Data,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
Data.pbData = rgbData;
|
|
Data.cbData = cbData;
|
|
if(!CryptSetHashParam(hHash,
|
|
HP_TLS1PRF_SEED,
|
|
(PBYTE)&Data,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbVerifyData,
|
|
&cbVerifyData,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
pctRet = PCT_ERR_OK;
|
|
|
|
|
|
error:
|
|
|
|
if(hHash)
|
|
{
|
|
if(!CryptDestroyHash(hHash))
|
|
{
|
|
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;
|
|
}
|
|
|