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.
562 lines
17 KiB
562 lines
17 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1995.
|
|
//
|
|
// File: ssl2msg.c
|
|
//
|
|
// Contents:
|
|
//
|
|
// Classes:
|
|
//
|
|
// Functions:
|
|
//
|
|
// History: 10-21-97 jbanes Added CAPI integration
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <spbase.h>
|
|
|
|
#if 0
|
|
Ssl2CipherMap Ssl2CipherRank[] =
|
|
{
|
|
{SSL_CK_RC4_128_WITH_MD5, CALG_MD5, CALG_RC4, 128, SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX},
|
|
{SSL_CK_DES_192_EDE3_CBC_WITH_MD5, CALG_MD5, CALG_3DES, 168, SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX},
|
|
{SSL_CK_RC2_128_CBC_WITH_MD5, CALG_MD5, CALG_RC2, 128, SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX},
|
|
{SSL_CK_RC4_128_FINANCE64_WITH_MD5, CALG_MD5, CALG_RC4, 64, SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX},
|
|
{SSL_CK_DES_64_CBC_WITH_MD5, CALG_MD5, CALG_DES, 56, SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX},
|
|
{SSL_CK_RC4_128_EXPORT40_WITH_MD5, CALG_MD5, CALG_RC4, 40, SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX},
|
|
{SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, CALG_MD5, CALG_RC2, 40, SP_EXCH_RSA_PKCS1, CALG_RSA_KEYX}
|
|
};
|
|
DWORD Ssl2NumCipherRanks = sizeof(Ssl2CipherRank)/sizeof(Ssl2CipherMap);
|
|
#endif
|
|
|
|
#if 0
|
|
CertTypeMap aSsl2CertEncodingPref[] =
|
|
{
|
|
{ X509_ASN_ENCODING, 0}
|
|
};
|
|
|
|
DWORD cSsl2CertEncodingPref = sizeof(aSsl2CertEncodingPref)/sizeof(CertTypeMap);
|
|
#endif
|
|
|
|
SP_STATUS WINAPI
|
|
Ssl2DecryptHandler(
|
|
PSPContext pContext,
|
|
PSPBuffer pCommInput,
|
|
PSPBuffer pAppOutput)
|
|
{
|
|
SP_STATUS pctRet = PCT_ERR_OK;
|
|
|
|
if (pCommInput->cbData > 0)
|
|
{
|
|
// First, we'll handle incoming data packets:
|
|
|
|
if ((pContext->State & SP_STATE_CONNECTED) && pContext->Decrypt)
|
|
{
|
|
pctRet = pContext->Decrypt(
|
|
pContext,
|
|
pCommInput, // message
|
|
pAppOutput); // Unpacked Message
|
|
if (PCT_ERR_OK == pctRet)
|
|
{
|
|
/* look for escapes */
|
|
}
|
|
return(pctRet);
|
|
}
|
|
else
|
|
{
|
|
return(SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG));
|
|
}
|
|
}
|
|
return (PCT_INT_INCOMPLETE_MSG);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Ssl2ComputeMac
|
|
//
|
|
// Synopsis: Compute an SSL2 message MAC.
|
|
//
|
|
// Arguments: [pContext] -- Schannel context.
|
|
//
|
|
// History: 10-22-97 jbanes Created.
|
|
//
|
|
// Notes: MAC_DATA := Hash(key + data + sequence_number)
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
static SP_STATUS
|
|
Ssl2ComputeMac(
|
|
PSPContext pContext, // in
|
|
BOOL fWriteMAC, // in
|
|
DWORD dwSequence, // in
|
|
PSPBuffer pData, // in
|
|
PBYTE pbMac, // out
|
|
DWORD cbMac) // in
|
|
{
|
|
DWORD dwReverseSequence;
|
|
BYTE rgbSalt[SP_MAX_MASTER_KEY];
|
|
DWORD cbSalt;
|
|
HCRYPTHASH hHash;
|
|
HCRYPTKEY hKey;
|
|
|
|
// Make sure output buffer is big enough.
|
|
if(cbMac < pContext->pHashInfo->cbCheckSum)
|
|
{
|
|
return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
|
|
}
|
|
|
|
dwReverseSequence = htonl(dwSequence);
|
|
|
|
hKey = fWriteMAC ? pContext->hWriteKey : pContext->hReadKey;
|
|
|
|
if(!CryptCreateHash(pContext->RipeZombie->hMasterProv,
|
|
pContext->pHashInfo->aiHash,
|
|
0,
|
|
0,
|
|
&hHash))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
SP_RETURN(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
|
|
if(!CryptHashSessionKey(hHash,
|
|
hKey,
|
|
CRYPT_LITTLE_ENDIAN))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
CryptDestroyHash(hHash);
|
|
SP_RETURN(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
cbSalt = sizeof(rgbSalt);
|
|
if(!CryptGetKeyParam(hKey,
|
|
KP_SALT,
|
|
rgbSalt,
|
|
&cbSalt,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
CryptDestroyHash(hHash);
|
|
SP_RETURN(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
if(!CryptHashData(hHash, rgbSalt, cbSalt, 0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
CryptDestroyHash(hHash);
|
|
SP_RETURN(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
|
|
if(!CryptHashData(hHash,
|
|
pData->pvBuffer,
|
|
pData->cbData,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
CryptDestroyHash(hHash);
|
|
SP_RETURN(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
|
|
if(!CryptHashData(hHash,
|
|
(PBYTE)&dwReverseSequence,
|
|
sizeof(DWORD),
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
CryptDestroyHash(hHash);
|
|
SP_RETURN(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
|
|
if(!CryptGetHashParam(hHash,
|
|
HP_HASHVAL,
|
|
pbMac,
|
|
&cbMac,
|
|
0))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
CryptDestroyHash(hHash);
|
|
SP_RETURN(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
|
|
CryptDestroyHash(hHash);
|
|
|
|
return PCT_ERR_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Ssl2EncryptMessage
|
|
//
|
|
// Synopsis: Encode a block of data as an SSL2 record.
|
|
//
|
|
// Arguments: [pContext] -- Schannel context.
|
|
// [pAppInput] -- Data to be encrypted.
|
|
// [pCommOutput] -- (output) Completed SSL2 record.
|
|
//
|
|
// History: 10-22-97 jbanes CAPI integrated.
|
|
//
|
|
// Notes: An SSL2 record is usually formatted as:
|
|
//
|
|
// BYTE header[2];
|
|
// BYTE mac[mac_size];
|
|
// BYTE data[pAppInput->cbData];
|
|
//
|
|
// If a block cipher is used, and the data to be encrypted
|
|
// consists of a partial number of blocks, then the following
|
|
// format is used:
|
|
//
|
|
// BYTE header[3];
|
|
// BYTE mac[mac_size];
|
|
// BYTE data[pAppInput->cbData];
|
|
// BYTE padding[padding_size];
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
SP_STATUS WINAPI
|
|
Ssl2EncryptMessage(
|
|
PSPContext pContext,
|
|
PSPBuffer pAppInput,
|
|
PSPBuffer pCommOutput)
|
|
{
|
|
SP_STATUS pctRet;
|
|
DWORD cPadding;
|
|
SPBuffer Clean;
|
|
SPBuffer Encrypted;
|
|
|
|
|
|
SP_BEGIN("Ssl2EncryptMessage");
|
|
|
|
/* Estimate if we have padding or not */
|
|
Encrypted.cbData = pAppInput->cbData + pContext->pHashInfo->cbCheckSum;
|
|
cPadding = (Encrypted.cbData % pContext->pCipherInfo->dwBlockSize);
|
|
if(cPadding)
|
|
{
|
|
cPadding = pContext->pCipherInfo->dwBlockSize - cPadding;
|
|
}
|
|
|
|
Encrypted.cbData += cPadding;
|
|
|
|
if(cPadding)
|
|
{
|
|
if(pCommOutput->cbBuffer + Encrypted.cbData + cPadding < 3)
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL));
|
|
}
|
|
Encrypted.pvBuffer = (PUCHAR)pCommOutput->pvBuffer + 3;
|
|
Encrypted.cbBuffer = pCommOutput->cbBuffer - 3;
|
|
}
|
|
else
|
|
{
|
|
if(pCommOutput->cbBuffer + Encrypted.cbData + cPadding < 2)
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL));
|
|
}
|
|
Encrypted.pvBuffer = (PUCHAR)pCommOutput->pvBuffer + 2;
|
|
Encrypted.cbBuffer = pCommOutput->cbBuffer - 2;
|
|
}
|
|
|
|
DebugLog((DEB_TRACE, "Sealing message %x\n", pContext->WriteCounter));
|
|
|
|
|
|
/* Move data out of the way if necessary */
|
|
if((PUCHAR)Encrypted.pvBuffer + pContext->pHashInfo->cbCheckSum != pAppInput->pvBuffer)
|
|
{
|
|
DebugLog((DEB_WARN, "SSL2EncryptMessage: Unnecessary Move, performance hog\n"));
|
|
/* if caller wasn't being smart, then we must copy memory here */
|
|
MoveMemory((PUCHAR)Encrypted.pvBuffer + pContext->pHashInfo->cbCheckSum,
|
|
pAppInput->pvBuffer,
|
|
pAppInput->cbData);
|
|
}
|
|
|
|
// Initialize pad
|
|
if(cPadding)
|
|
{
|
|
FillMemory((PUCHAR)Encrypted.pvBuffer + pContext->pHashInfo->cbCheckSum + pAppInput->cbData, cPadding, 0);
|
|
}
|
|
|
|
// Compute MAC.
|
|
Clean.pvBuffer = (PBYTE)Encrypted.pvBuffer + pContext->pHashInfo->cbCheckSum;
|
|
Clean.cbData = Encrypted.cbData - pContext->pHashInfo->cbCheckSum;
|
|
Clean.cbBuffer = Clean.cbData;
|
|
|
|
pctRet = Ssl2ComputeMac(pContext,
|
|
TRUE,
|
|
pContext->WriteCounter,
|
|
&Clean,
|
|
Encrypted.pvBuffer,
|
|
pContext->pHashInfo->cbCheckSum);
|
|
if(pctRet != PCT_ERR_OK)
|
|
{
|
|
SP_RETURN(pctRet);
|
|
}
|
|
|
|
// Encrypt buffer.
|
|
if(!CryptEncrypt(pContext->hWriteKey,
|
|
0, FALSE, 0,
|
|
Encrypted.pvBuffer,
|
|
&Encrypted.cbData,
|
|
Encrypted.cbBuffer))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
SP_RETURN(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
|
|
/* set sizes */
|
|
if(cPadding)
|
|
{
|
|
if(Encrypted.cbData > 0x3fff)
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_DATA_OVERFLOW));
|
|
}
|
|
|
|
((PUCHAR)pCommOutput->pvBuffer)[0]= (UCHAR)(0x3f & (Encrypted.cbData>>8));
|
|
((PUCHAR)pCommOutput->pvBuffer)[1]= (UCHAR)(0xff & Encrypted.cbData);
|
|
((PUCHAR)pCommOutput->pvBuffer)[2]= (UCHAR)cPadding;
|
|
|
|
}
|
|
else
|
|
{
|
|
if(Encrypted.cbData > 0x7fff)
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_DATA_OVERFLOW));
|
|
}
|
|
((PUCHAR)pCommOutput->pvBuffer)[0]= (UCHAR)(0x7f & (Encrypted.cbData>>8)) | 0x80;
|
|
((PUCHAR)pCommOutput->pvBuffer)[1]= (UCHAR)(0xff & Encrypted.cbData);
|
|
}
|
|
|
|
pCommOutput->cbData = Encrypted.cbData + (cPadding?3:2);
|
|
|
|
pContext->WriteCounter ++ ;
|
|
|
|
SP_RETURN( PCT_ERR_OK );
|
|
}
|
|
|
|
SP_STATUS WINAPI
|
|
Ssl2GetHeaderSize(
|
|
PSPContext pContext,
|
|
PSPBuffer pCommInput,
|
|
DWORD * pcbHeaderSize)
|
|
{
|
|
if(pcbHeaderSize == NULL)
|
|
{
|
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
if(pCommInput->cbData < 1)
|
|
{
|
|
return (PCT_INT_INCOMPLETE_MSG);
|
|
}
|
|
if( ((PUCHAR)pCommInput->pvBuffer)[0]&0x80 )
|
|
{
|
|
*pcbHeaderSize = 2 + pContext->pHashInfo->cbCheckSum;
|
|
}
|
|
else
|
|
{
|
|
*pcbHeaderSize = 3 + pContext->pHashInfo->cbCheckSum;
|
|
}
|
|
return PCT_ERR_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Ssl2DecryptMessage
|
|
//
|
|
// Synopsis: Decode an SSL2 record.
|
|
//
|
|
// Arguments: [pContext] -- Schannel context.
|
|
// [pMessage] -- Data from the remote party.
|
|
// [pAppOutput] -- (output) Decrypted data.
|
|
//
|
|
// History: 10-22-97 jbanes CAPI integrated.
|
|
//
|
|
// Notes: An SSL2 record is usually formatted as:
|
|
//
|
|
// BYTE header[2];
|
|
// BYTE mac[mac_size];
|
|
// BYTE data[pAppInput->cbData];
|
|
//
|
|
// If a block cipher is used, and the data to be encrypted
|
|
// consists of a partial number of blocks, then the following
|
|
// format is used:
|
|
//
|
|
// BYTE header[3];
|
|
// BYTE mac[mac_size];
|
|
// BYTE data[pAppInput->cbData];
|
|
// BYTE padding[padding_size];
|
|
//
|
|
// The number of input data bytes consumed by this function
|
|
// is returned in pMessage->cbData.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
SP_STATUS WINAPI
|
|
Ssl2DecryptMessage(
|
|
PSPContext pContext,
|
|
PSPBuffer pMessage,
|
|
PSPBuffer pAppOutput)
|
|
{
|
|
SP_STATUS pctRet;
|
|
DWORD cPadding;
|
|
DWORD dwLength;
|
|
SPBuffer Encrypted;
|
|
SPBuffer Clean;
|
|
DWORD cbActualData;
|
|
UCHAR Digest[SP_MAX_DIGEST_LEN];
|
|
|
|
SP_BEGIN("Ssl2DecryptMessage");
|
|
|
|
/* 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 = 2; /* minimum amount of data we need */
|
|
|
|
if(pMessage->cbData > cbActualData)
|
|
{
|
|
SP_RETURN(PCT_INT_INCOMPLETE_MSG);
|
|
}
|
|
DebugLog((DEB_TRACE, " Incomming Buffer: %lx, size %ld (%lx)\n", pMessage->pvBuffer, cbActualData, cbActualData));
|
|
|
|
if(((PUCHAR)pMessage->pvBuffer)[0] & 0x80)
|
|
{
|
|
/* 2 byte header */
|
|
cPadding = 0;
|
|
dwLength = ((((PUCHAR)pMessage->pvBuffer)[0] & 0x7f) << 8) |
|
|
((PUCHAR)pMessage->pvBuffer)[1];
|
|
|
|
Encrypted.pvBuffer = (PUCHAR)pMessage->pvBuffer + 2;
|
|
Encrypted.cbBuffer = pMessage->cbBuffer - 2;
|
|
}
|
|
else
|
|
{
|
|
pMessage->cbData++;
|
|
if(pMessage->cbData > cbActualData)
|
|
{
|
|
SP_RETURN(PCT_INT_INCOMPLETE_MSG);
|
|
}
|
|
|
|
/* 3 byte header */
|
|
cPadding = ((PUCHAR)pMessage->pvBuffer)[2];
|
|
dwLength = ((((PUCHAR)pMessage->pvBuffer)[0] & 0x3f) << 8) |
|
|
((PUCHAR)pMessage->pvBuffer)[1];
|
|
|
|
Encrypted.pvBuffer = (PUCHAR)pMessage->pvBuffer + 3;
|
|
Encrypted.cbBuffer = pMessage->cbBuffer - 3;
|
|
}
|
|
|
|
/* Now we know how mutch data we will eat, so set cbData on the Input to be that size */
|
|
pMessage->cbData += dwLength;
|
|
|
|
/* do we have enough bytes for the reported data */
|
|
if(pMessage->cbData > cbActualData)
|
|
{
|
|
SP_RETURN(PCT_INT_INCOMPLETE_MSG);
|
|
}
|
|
|
|
/* do we have enough data for our checksum */
|
|
if(dwLength < pContext->pHashInfo->cbCheckSum + cPadding)
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_MSG_ALTERED));
|
|
}
|
|
|
|
Encrypted.cbData = dwLength; /* encrypted data size */
|
|
Encrypted.cbBuffer = Encrypted.cbData;
|
|
|
|
/* check to see if we have a block size violation */
|
|
if(Encrypted.cbData % pContext->pCipherInfo->dwBlockSize)
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_MSG_ALTERED));
|
|
}
|
|
|
|
/* Decrypt */
|
|
if(!CryptDecrypt(pContext->hReadKey,
|
|
0, FALSE, 0,
|
|
Encrypted.pvBuffer,
|
|
&Encrypted.cbData))
|
|
{
|
|
SP_LOG_RESULT(GetLastError());
|
|
SP_RETURN(PCT_INT_INTERNAL_ERROR);
|
|
}
|
|
|
|
// Compute MAC.
|
|
Clean.pvBuffer = (PBYTE)Encrypted.pvBuffer + pContext->pHashInfo->cbCheckSum;
|
|
Clean.cbData = Encrypted.cbData - pContext->pHashInfo->cbCheckSum;
|
|
Clean.cbBuffer = Clean.cbData;
|
|
|
|
pctRet = Ssl2ComputeMac(pContext,
|
|
FALSE,
|
|
pContext->ReadCounter,
|
|
&Clean,
|
|
Digest,
|
|
sizeof(Digest));
|
|
if(pctRet != PCT_ERR_OK)
|
|
{
|
|
SP_RETURN(pctRet);
|
|
}
|
|
|
|
// the padding is computed in the hash but is not needed after this
|
|
Clean.cbData -= cPadding;
|
|
|
|
DebugLog((DEB_TRACE, "Unsealing message %x\n", pContext->ReadCounter));
|
|
|
|
pContext->ReadCounter++;
|
|
|
|
if(memcmp(Digest, Encrypted.pvBuffer, pContext->pHashInfo->cbCheckSum ) )
|
|
{
|
|
SP_RETURN(SP_LOG_RESULT(PCT_INT_MSG_ALTERED));
|
|
}
|
|
|
|
if(pAppOutput->pvBuffer != Clean.pvBuffer)
|
|
{
|
|
DebugLog((DEB_WARN, "SSL2DecryptMessage: Unnecessary Move, performance hog\n"));
|
|
MoveMemory(pAppOutput->pvBuffer,
|
|
Clean.pvBuffer,
|
|
Clean.cbData);
|
|
}
|
|
pAppOutput->cbData = Clean.cbData;
|
|
DebugLog((DEB_TRACE, " TotalData: size %ld (%lx)\n", pMessage->cbData, pMessage->cbData));
|
|
|
|
SP_RETURN( PCT_ERR_OK );
|
|
}
|
|
|
|
#if 0
|
|
SP_STATUS
|
|
Ssl2MakeMasterKeyBlock(PSPContext pContext)
|
|
{
|
|
|
|
MD5_CTX Md5Hash;
|
|
UCHAR cSalt;
|
|
UCHAR ib;
|
|
|
|
|
|
//pContext->RipeZombe->pMasterKey containst the master secret.
|
|
|
|
#if DBG
|
|
DebugLog((DEB_TRACE, " Master Secret\n"));
|
|
DBG_HEX_STRING(DEB_TRACE,pContext->RipeZombie->pMasterKey, pContext->RipeZombie->cbMasterKey);
|
|
|
|
#endif
|
|
|
|
for(ib=0 ; ib<3 ; ib++)
|
|
{
|
|
// MD5(master_secret + SHA-hash)
|
|
MD5Init (&Md5Hash);
|
|
MD5Update(&Md5Hash, pContext->RipeZombie->pMasterKey, pContext->RipeZombie->cbMasterKey);
|
|
|
|
// We're going to be bug-for-bug compatable with netscape, so
|
|
// we always add the digit into the hash, instead of following
|
|
// the spec which says don't add the digit for DES
|
|
//if(pContext->RipeZombie->aiCipher != CALG_DES)
|
|
{
|
|
cSalt = ib+'0';
|
|
MD5Update(&Md5Hash, &cSalt, 1);
|
|
}
|
|
MD5Update(&Md5Hash, pContext->pChallenge, pContext->cbChallenge);
|
|
MD5Update(&Md5Hash, pContext->pConnectionID, pContext->cbConnectionID);
|
|
MD5Final (&Md5Hash);
|
|
CopyMemory(pContext->Ssl3MasterKeyBlock + ib * MD5DIGESTLEN, Md5Hash.digest, MD5DIGESTLEN);
|
|
}
|
|
#if DBG
|
|
DebugLog((DEB_TRACE, " Master Key Block\n"));
|
|
DBG_HEX_STRING(DEB_TRACE,pContext->Ssl3MasterKeyBlock, MD5DIGESTLEN*3);
|
|
|
|
#endif
|
|
return( PCT_ERR_OK );
|
|
}
|
|
#endif
|
|
|