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

550 lines
14 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*-----------------------------------------------------------------------------
* Copyright (C) Microsoft Corporation, 1995 - 1996.
* All rights reserved.
*
* Owner : ramas
* Date : 5/03/97
* description : Main Crypto functions for TLS1
*----------------------------------------------------------------------------*/
#include <spbase.h>
#define DEB_TLS1KEYS 0x01000000
//+---------------------------------------------------------------------------
//
// Function: Tls1MakeWriteSessionKeys
//
// Synopsis:
//
// Arguments: [pContext] -- Schannel context.
//
// History: 10-10-97 jbanes Added server-side CAPI integration.
//
// Notes:
//
//----------------------------------------------------------------------------
SP_STATUS
Tls1MakeWriteSessionKeys(PSPContext pContext)
{
BOOL fClient;
// Determine if we're a client or a server.
fClient = (0 != (pContext->RipeZombie->fProtocol & SP_PROT_TLS1_CLIENT));
if(pContext->hWriteKey)
{
if(!SchCryptDestroyKey(pContext->hWriteKey,
pContext->RipeZombie->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
}
}
pContext->hWriteProv = pContext->RipeZombie->hMasterProv;
pContext->hWriteKey = pContext->hPendingWriteKey;
pContext->hPendingWriteKey = 0;
if(pContext->hWriteMAC)
{
if(!SchCryptDestroyKey(pContext->hWriteMAC,
pContext->RipeZombie->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
}
}
pContext->hWriteMAC = pContext->hPendingWriteMAC;
pContext->hPendingWriteMAC = 0;
return PCT_ERR_OK;
}
//+---------------------------------------------------------------------------
//
// Function: Tls1MakeReadSessionKeys
//
// Synopsis:
//
// Arguments: [pContext] -- Schannel context.
//
// History: 10-10-97 jbanes Added server-side CAPI integration.
//
// Notes:
//
//----------------------------------------------------------------------------
SP_STATUS
Tls1MakeReadSessionKeys(PSPContext pContext)
{
BOOL fClient;
// Determine if we're a client or a server.
fClient = (0 != (pContext->RipeZombie->fProtocol & SP_PROT_TLS1_CLIENT));
if(pContext->hReadKey)
{
if(!SchCryptDestroyKey(pContext->hReadKey,
pContext->RipeZombie->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
}
}
pContext->hReadProv = pContext->RipeZombie->hMasterProv;
pContext->hReadKey = pContext->hPendingReadKey;
pContext->hPendingReadKey = 0;
if(pContext->hReadMAC)
{
if(!SchCryptDestroyKey(pContext->hReadMAC,
pContext->RipeZombie->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
}
}
pContext->hReadMAC = pContext->hPendingReadMAC;
pContext->hPendingReadMAC = 0;
return PCT_ERR_OK;
}
//+---------------------------------------------------------------------------
//
// Function: Tls1ComputeMac
//
// Synopsis:
//
// Arguments: [pContext] --
// [hSecret] --
// [dwSequence] --
// [pClean] --
// [cContentType] --
// [pbMac] --
// [cbMac]
//
// History: 10-03-97 jbanes Created.
//
// Notes:
//
//----------------------------------------------------------------------------
SP_STATUS
Tls1ComputeMac(
PSPContext pContext,
BOOL fReadMac,
PSPBuffer pClean,
CHAR cContentType,
PBYTE pbMac,
DWORD cbMac)
{
HCRYPTHASH hHash;
HMAC_INFO HmacInfo;
PBYTE pbData;
DWORD cbData;
DWORD cbDataReverse;
DWORD dwReverseSequence;
UCHAR rgbData1[15];
PUCHAR pbData1;
DWORD cbData1;
HCRYPTPROV hProv;
HCRYPTKEY hSecret;
DWORD dwSequence;
DWORD dwCapiFlags;
PHashInfo pHashInfo;
pbData = pClean->pvBuffer;
cbData = pClean->cbData;
if(cbData & 0xFFFF0000)
{
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
}
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);
}
// Create hash object.
if(!SchCryptCreateHash(hProv,
CALG_HMAC,
hSecret,
0,
&hHash,
dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
return PCT_INT_INTERNAL_ERROR;
}
// Specify hash algorithm.
ZeroMemory(&HmacInfo, sizeof(HMAC_INFO));
HmacInfo.HashAlgid = pHashInfo->aiHash;
if(!SchCryptSetHashParam(hHash,
HP_HMAC_INFO,
(PBYTE)&HmacInfo,
0,
dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
SchCryptDestroyHash(hHash, dwCapiFlags);
return PCT_INT_INTERNAL_ERROR;
}
// Build data to be hashed.
cbData1 = 2 * sizeof(DWORD) + // sequence number (64-bit)
1 + // content type
2 + // protocol version
2; // message length
SP_ASSERT(cbData1 <= sizeof(rgbData1));
pbData1 = rgbData1;
ZeroMemory(pbData1, sizeof(DWORD));
pbData1 += sizeof(DWORD);
dwReverseSequence = htonl(dwSequence);
CopyMemory(pbData1, &dwReverseSequence, sizeof(DWORD));
pbData1 += sizeof(DWORD);
*pbData1++ = cContentType;
*pbData1++ = SSL3_CLIENT_VERSION_MSB;
*pbData1++ = TLS1_CLIENT_VERSION_LSB;
cbDataReverse = (cbData >> 8) | (cbData << 8);
CopyMemory(pbData1, &cbDataReverse, 2);
// Hash data.
if(!SchCryptHashData(hHash,
rgbData1, cbData1,
0,
dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
SchCryptDestroyHash(hHash, dwCapiFlags);
return PCT_INT_INTERNAL_ERROR;
}
if(!SchCryptHashData(hHash,
pbData, cbData,
0,
dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
SchCryptDestroyHash(hHash, dwCapiFlags);
return PCT_INT_INTERNAL_ERROR;
}
// Get hash value.
if(!SchCryptGetHashParam(hHash,
HP_HASHVAL,
pbMac,
&cbMac,
0,
dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
SchCryptDestroyHash(hHash, dwCapiFlags);
return PCT_INT_INTERNAL_ERROR;
}
SP_ASSERT(cbMac == pHashInfo->cbCheckSum);
#if DBG
DebugLog((DEB_TLS1KEYS, " TLS1 MAC Output"));
DBG_HEX_STRING(DEB_TLS1KEYS, pbMac, cbMac);
#endif
SchCryptDestroyHash(hHash, dwCapiFlags);
return PCT_ERR_OK;
}
#define HMAC_K_PADSIZE 64
BOOL MyPrimitiveSHA(
PBYTE pbData,
DWORD cbData,
BYTE rgbHash[A_SHA_DIGEST_LEN])
{
BOOL fRet = FALSE;
A_SHA_CTX sSHAHash;
A_SHAInit(&sSHAHash);
A_SHAUpdate(&sSHAHash, (BYTE *) pbData, cbData);
A_SHAFinal(&sSHAHash, rgbHash);
fRet = TRUE;
//Ret:
return fRet;
}
BOOL MyPrimitiveMD5(
PBYTE pbData,
DWORD cbData,
BYTE rgbHash[MD5DIGESTLEN])
{
BOOL fRet = FALSE;
MD5_CTX sMD5Hash;
MD5Init(&sMD5Hash);
MD5Update(&sMD5Hash, (BYTE *) pbData, cbData);
MD5Final(&sMD5Hash);
memcpy(rgbHash, sMD5Hash.digest, MD5DIGESTLEN);
fRet = TRUE;
//Ret:
return fRet;
}
BOOL MyPrimitiveHMACParam(
PBYTE pbKeyMaterial,
DWORD cbKeyMaterial,
PBYTE pbData,
DWORD cbData,
ALG_ID Algid,
BYTE rgbHMAC[A_SHA_DIGEST_LEN])
{
BYTE rgbFirstHash[A_SHA_DIGEST_LEN];
BYTE rgbHMACTmp[HMAC_K_PADSIZE+A_SHA_DIGEST_LEN];
BOOL fRet = FALSE;
BYTE rgbKipad[HMAC_K_PADSIZE];
BYTE rgbKopad[HMAC_K_PADSIZE];
DWORD dwBlock;
// truncate
if (cbKeyMaterial > HMAC_K_PADSIZE)
cbKeyMaterial = HMAC_K_PADSIZE;
ZeroMemory(rgbKipad, HMAC_K_PADSIZE);
CopyMemory(rgbKipad, pbKeyMaterial, cbKeyMaterial);
ZeroMemory(rgbKopad, HMAC_K_PADSIZE);
CopyMemory(rgbKopad, pbKeyMaterial, cbKeyMaterial);
// Kipad, Kopad are padded sMacKey. Now XOR across...
for(dwBlock=0; dwBlock<HMAC_K_PADSIZE/sizeof(DWORD); dwBlock++)
{
((DWORD*)rgbKipad)[dwBlock] ^= 0x36363636;
((DWORD*)rgbKopad)[dwBlock] ^= 0x5C5C5C5C;
}
// prepend Kipad to data, Hash to get H1
if (CALG_SHA1 == Algid)
{
// do this inline since it would require data copy
A_SHA_CTX sSHAHash;
A_SHAInit(&sSHAHash);
A_SHAUpdate(&sSHAHash, rgbKipad, HMAC_K_PADSIZE);
A_SHAUpdate(&sSHAHash, pbData, cbData);
// Finish off the hash
A_SHAFinal(&sSHAHash, sSHAHash.HashVal);
// prepend Kopad to H1, hash to get HMAC
CopyMemory(rgbHMACTmp, rgbKopad, HMAC_K_PADSIZE);
CopyMemory(rgbHMACTmp+HMAC_K_PADSIZE, sSHAHash.HashVal, A_SHA_DIGEST_LEN);
if (!MyPrimitiveSHA(
rgbHMACTmp,
HMAC_K_PADSIZE + A_SHA_DIGEST_LEN,
rgbHMAC))
goto Ret;
}
else
{
// do this inline since it would require data copy
MD5_CTX sMD5Hash;
MD5Init(&sMD5Hash);
MD5Update(&sMD5Hash, rgbKipad, HMAC_K_PADSIZE);
MD5Update(&sMD5Hash, pbData, cbData);
MD5Final(&sMD5Hash);
// prepend Kopad to H1, hash to get HMAC
CopyMemory(rgbHMACTmp, rgbKopad, HMAC_K_PADSIZE);
CopyMemory(rgbHMACTmp+HMAC_K_PADSIZE, sMD5Hash.digest, MD5DIGESTLEN);
if (!MyPrimitiveMD5(
rgbHMACTmp,
HMAC_K_PADSIZE + MD5DIGESTLEN,
rgbHMAC))
goto Ret;
}
fRet = TRUE;
Ret:
return fRet;
}
//+ ---------------------------------------------------------------------
// the P_Hash algorithm from TLS
BOOL P_Hash
(
PBYTE pbSecret,
DWORD cbSecret,
PBYTE pbSeed,
DWORD cbSeed,
ALG_ID Algid,
PBYTE pbKeyOut, //Buffer to copy the result...
DWORD cbKeyOut //# of bytes of key length they want as output.
)
{
BOOL fRet = FALSE;
BYTE rgbDigest[A_SHA_DIGEST_LEN];
DWORD iKey;
DWORD cbHash;
PBYTE pbAofiDigest = NULL;
if (NULL == (pbAofiDigest = SPExternalAlloc(cbSeed + A_SHA_DIGEST_LEN)))
goto Ret;
if (CALG_SHA1 == Algid)
{
cbHash = A_SHA_DIGEST_LEN;
}
else
{
cbHash = MD5DIGESTLEN;
}
// First, we define a data expansion function, P_hash(secret, data)
// which uses a single hash function to expand a secret and seed into
// an arbitrary quantity of output:
// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
// HMAC_hash(secret, A(2) + seed) +
// HMAC_hash(secret, A(3) + seed) + ...
// Where + indicates concatenation.
// A() is defined as:
// A(0) = seed
// A(i) = HMAC_hash(secret, A(i-1))
// build A(1)
if (!MyPrimitiveHMACParam(pbSecret, cbSecret, pbSeed, cbSeed,
Algid, pbAofiDigest))
goto Ret;
// create Aofi: ( A(i) | seed )
CopyMemory(&pbAofiDigest[cbHash], pbSeed, cbSeed);
for (iKey=0; cbKeyOut; iKey++)
{
// build Digest = HMAC(key | A(i) | seed);
if (!MyPrimitiveHMACParam(pbSecret, cbSecret, pbAofiDigest,
cbSeed + cbHash, Algid, rgbDigest))
goto Ret;
// append to pbKeyOut
if(cbKeyOut < cbHash)
{
CopyMemory(pbKeyOut, rgbDigest, cbKeyOut);
break;
}
else
{
CopyMemory(pbKeyOut, rgbDigest, cbHash);
pbKeyOut += cbHash;
}
cbKeyOut -= cbHash;
// build A(i) = HMAC(key, A(i-1))
if (!MyPrimitiveHMACParam(pbSecret, cbSecret, pbAofiDigest, cbHash,
Algid, pbAofiDigest))
goto Ret;
}
fRet = TRUE;
Ret:
if (pbAofiDigest)
SPExternalFree(pbAofiDigest);
return fRet;
}
BOOL PRF(
PBYTE pbSecret,
DWORD cbSecret,
PBYTE pbLabel,
DWORD cbLabel,
PBYTE pbSeed,
DWORD cbSeed,
PBYTE pbKeyOut, //Buffer to copy the result...
DWORD cbKeyOut //# of bytes of key length they want as output.
)
{
BYTE *pbBuff = NULL;
BYTE *pbLabelAndSeed = NULL;
DWORD cbLabelAndSeed;
DWORD cbOdd;
DWORD cbHalfSecret;
DWORD i;
BOOL fRet = FALSE;
cbOdd = cbSecret % 2;
cbHalfSecret = cbSecret / 2;
cbLabelAndSeed = cbLabel + cbSeed;
if (NULL == (pbLabelAndSeed = SPExternalAlloc(cbLabelAndSeed)))
goto Ret;
if (NULL == (pbBuff = SPExternalAlloc(cbKeyOut)))
goto Ret;
// copy label and seed into one buffer
memcpy(pbLabelAndSeed, pbLabel, cbLabel);
memcpy(pbLabelAndSeed + cbLabel, pbSeed, cbSeed);
// Use P_hash to calculate MD5 half
if (!P_Hash(pbSecret, cbHalfSecret + cbOdd, pbLabelAndSeed,
cbLabelAndSeed, CALG_MD5, pbKeyOut, cbKeyOut))
goto Ret;
// Use P_hash to calculate SHA half
if (!P_Hash(pbSecret + cbHalfSecret, cbHalfSecret + cbOdd, pbLabelAndSeed,
cbLabelAndSeed, CALG_SHA1, pbBuff, cbKeyOut))
goto Ret;
// XOR the two halves
for (i=0;i<cbKeyOut;i++)
{
pbKeyOut[i] = pbKeyOut[i] ^ pbBuff[i];
}
fRet = TRUE;
Ret:
if (pbBuff)
SPExternalFree(pbBuff);
if (pbLabelAndSeed)
SPExternalFree(pbLabelAndSeed);
return fRet;
}