|
|
/*-----------------------------------------------------------------------------
* 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(!CryptDestroyKey(pContext->hWriteKey)) { SP_LOG_RESULT(GetLastError()); } } pContext->hWriteProv = pContext->RipeZombie->hMasterProv; pContext->hWriteKey = pContext->hPendingWriteKey; pContext->hPendingWriteKey = 0;
if(pContext->hWriteMAC) { if(!CryptDestroyKey(pContext->hWriteMAC)) { 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(!CryptDestroyKey(pContext->hReadKey)) { SP_LOG_RESULT(GetLastError()); } } pContext->hReadProv = pContext->RipeZombie->hMasterProv; pContext->hReadKey = pContext->hPendingReadKey; pContext->hPendingReadKey = 0;
if(pContext->hReadMAC) { if(!CryptDestroyKey(pContext->hReadMAC)) { 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; 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; }
if(!hProv) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
// Create hash object.
if(!CryptCreateHash(hProv, CALG_HMAC, hSecret, 0, &hHash)) { SP_LOG_RESULT(GetLastError()); return PCT_INT_INTERNAL_ERROR; }
// Specify hash algorithm.
ZeroMemory(&HmacInfo, sizeof(HMAC_INFO)); HmacInfo.HashAlgid = pHashInfo->aiHash; if(!CryptSetHashParam(hHash, HP_HMAC_INFO, (PBYTE)&HmacInfo, 0)) { SP_LOG_RESULT(GetLastError()); CryptDestroyHash(hHash); 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(!CryptHashData(hHash, rgbData1, cbData1, 0)) { SP_LOG_RESULT(GetLastError()); CryptDestroyHash(hHash); return PCT_INT_INTERNAL_ERROR; } if(!CryptHashData(hHash, pbData, cbData, 0)) { SP_LOG_RESULT(GetLastError()); CryptDestroyHash(hHash); return PCT_INT_INTERNAL_ERROR; }
// Get hash value.
if(!CryptGetHashParam(hHash, HP_HASHVAL, pbMac, &cbMac, 0)) { SP_LOG_RESULT(GetLastError()); CryptDestroyHash(hHash); 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
CryptDestroyHash(hHash);
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 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; BYTE HashVal[A_SHA_DIGEST_LEN];
A_SHAInit(&sSHAHash); A_SHAUpdate(&sSHAHash, rgbKipad, HMAC_K_PADSIZE); A_SHAUpdate(&sSHAHash, pbData, cbData);
// Finish off the hash
A_SHAFinal(&sSHAHash, HashVal);
// prepend Kopad to H1, hash to get HMAC
CopyMemory(rgbHMACTmp, rgbKopad, HMAC_K_PADSIZE); CopyMemory(rgbHMACTmp+HMAC_K_PADSIZE, 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;
SafeAllocaAllocate(pbAofiDigest, cbSeed + A_SHA_DIGEST_LEN); if (NULL == pbAofiDigest) 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) SafeAllocaFree(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; SafeAllocaAllocate(pbLabelAndSeed, cbLabelAndSeed); if (NULL == pbLabelAndSeed) goto Ret; SafeAllocaAllocate(pbBuff, cbKeyOut); if (NULL == pbBuff) 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) SafeAllocaFree(pbBuff); if (pbLabelAndSeed) SafeAllocaFree(pbLabelAndSeed); return fRet; }
|