Leaked source code of windows server 2003
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.
 
 
 
 
 
 

714 lines
17 KiB

/*
File: capiprim.cpp
Title: Cryptographic Primitives using CryptoAPI
Author: Matt Thomlinson
Date: 11/22/96
Functions in this module:
FMyPrimitiveCryptHMAC
Derives a quality HMAC (Keyed message-authentication code).
The HMAC is computed in the following (standard HMAC) manner:
KoPad = KiPad = DESKey key setup buffer
XOR(KoPad, 0x5c5c5c5c)
XOR(KiPad, 0x36363636)
HMAC = SHA1(KoPad | SHA1(KiPad | Data))
*/
#include <pch.cpp>
#pragma hdrstop
#include "crypt.h"
BOOL
WINAPI
_CryptEnumProvidersW(
DWORD dwIndex,
DWORD * pdwReserved,
DWORD dwFlags,
DWORD * pdwProvType,
LPWSTR pszProvName,
DWORD * pcbProvName
);
extern DWORD g_dwCryptProviderID;
#define HMAC_K_PADSIZE 64
extern CCryptProvList* g_pCProvList;
BOOL FMyPrimitiveCryptHMAC(
PBYTE pbKeyMaterial,
DWORD cbKeyMaterial,
PBYTE pbData,
DWORD cbData,
HCRYPTPROV hVerifyProv,
DWORD dwHashAlg,
HCRYPTHASH* phHash) // out
{
DWORD cb;
BOOL fRet = FALSE;
BYTE rgbKipad[HMAC_K_PADSIZE]; ZeroMemory(rgbKipad, HMAC_K_PADSIZE);
BYTE rgbKopad[HMAC_K_PADSIZE]; ZeroMemory(rgbKopad, HMAC_K_PADSIZE);
BYTE rgbHMACTmp[HMAC_K_PADSIZE+A_SHA_DIGEST_LEN];
HCRYPTHASH hTmpHash = NULL;
// truncate
if (cbKeyMaterial > HMAC_K_PADSIZE)
cbKeyMaterial = HMAC_K_PADSIZE;
// fill pad bufs with keying material
CopyMemory(rgbKipad, pbKeyMaterial, cbKeyMaterial);
CopyMemory(rgbKopad, pbKeyMaterial, cbKeyMaterial);
// assert we're a multiple for the next loop
SS_ASSERT( (HMAC_K_PADSIZE % sizeof(DWORD)) == 0);
// Kipad, Kopad are padded sMacKey. Now XOR across...
for(DWORD dwBlock=0; dwBlock<HMAC_K_PADSIZE/sizeof(DWORD); dwBlock++)
{
((DWORD*)rgbKipad)[dwBlock] ^= 0x36363636;
((DWORD*)rgbKopad)[dwBlock] ^= 0x5C5C5C5C;
}
// check passed-in prov
if (hVerifyProv == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
goto Ret;
}
// create an intermediate hash
if (!CryptCreateHash(
hVerifyProv,
dwHashAlg,
NULL,
0,
&hTmpHash))
goto Ret;
// prepend Kipad to data, Hash to get H1
if (!CryptHashData(
hTmpHash,
rgbKipad,
sizeof(rgbKipad),
0))
goto Ret;
if (!CryptHashData(
hTmpHash,
pbData,
cbData,
0))
goto Ret;
// prepend Kopad to H1
CopyMemory(rgbHMACTmp, rgbKopad, HMAC_K_PADSIZE);
cb = A_SHA_DIGEST_LEN;
if (!CryptGetHashParam(
hTmpHash,
HP_HASHVAL,
rgbHMACTmp+HMAC_K_PADSIZE,
&cb,
0))
goto Ret;
// do final hash w/ CryptoAPI into output hash
// create the final hash
if (!CryptCreateHash(
hVerifyProv,
dwHashAlg,
NULL,
0,
phHash))
goto Ret;
// hash ( Kopad | H1 ) to get HMAC
if (!CryptHashData(
*phHash,
rgbHMACTmp,
HMAC_K_PADSIZE + cb, // pad + hashsize
0))
goto Ret;
fRet = TRUE;
Ret:
if (hTmpHash)
CryptDestroyHash(hTmpHash);
return fRet;
}
#if DBG
void CheckMACInterop(
PBYTE pbMonsterKey,
DWORD cbMonsterKey,
PBYTE pbRandKey,
DWORD cbRandKey,
HCRYPTPROV hVerifyProv,
ALG_ID algidHash)
{
HCRYPTHASH hHash = 0;
BOOL fRet = FALSE;
BYTE rgbOldHash[A_SHA_DIGEST_LEN];
BYTE rgbCryptHash[A_SHA_DIGEST_LEN];
DWORD cbHashSize = sizeof(rgbCryptHash);
if (algidHash == CALG_SHA1)
{
if (!FMyPrimitiveCryptHMAC(
pbMonsterKey, // key
cbMonsterKey,
pbRandKey, // data
cbRandKey,
hVerifyProv,
algidHash,
&hHash)) // output
goto Ret;
// nab crypt result
if (!CryptGetHashParam(
hHash,
HP_HASHVAL,
rgbCryptHash,
&cbHashSize,
0))
goto Ret;
// nab raw result
if (!FMyPrimitiveHMACParam(
pbMonsterKey,
cbMonsterKey,
pbRandKey,
cbRandKey,
rgbOldHash))
goto Ret;
if (0 != memcmp(rgbOldHash, rgbCryptHash, A_SHA_DIGEST_LEN))
goto Ret;
}
// else don't have interop test
fRet = TRUE;
Ret:
if (!fRet)
{
OutputDebugString(TEXT("HMACs did not interop!!!!\n"));
SS_ASSERT(0);
}
if (hHash)
CryptDestroyHash(hHash);
return;
}
#endif // DBG
// USEC -- (US Export Controls)
DWORD GetSaltForExportControl(
HCRYPTPROV hProv,
HCRYPTKEY hKey,
PBYTE* ppbSalt,
DWORD* pcbSalt)
{
DWORD dwRet;
// fix bug: derived keys will be > 40 bits in length
// fix is to stomp derived key with exposed salt (Provider knows what is legal!!)
if (!CryptGetKeyParam(
hKey,
KP_SALT,
NULL,
pcbSalt,
0))
{
#if DBG
if (GetLastError() != NTE_BAD_KEY)
OutputDebugString(TEXT("GetSaltForExportControl failed in possible violation of ITAR!\n"));
#endif
/*
dwRet = GetLastError();
goto Ret;
*/
// Assume key type doesn't support salt
// report cbSalt = 0
*pcbSalt = 0;
*ppbSalt = (PBYTE)SSAlloc(0);
dwRet = ERROR_SUCCESS;
goto Ret;
}
*ppbSalt = (PBYTE)SSAlloc(*pcbSalt);
if (!RtlGenRandom(
*ppbSalt,
*pcbSalt))
{
dwRet = GetLastError();
goto Ret;
}
// don't get if salt zero len (bug workaround for NT5B1 RSAEnh)
if (*pcbSalt != 0)
{
if (!CryptSetKeyParam(
hKey,
KP_SALT,
*ppbSalt,
0))
{
dwRet = GetLastError();
goto Ret;
}
}
dwRet = ERROR_SUCCESS;
Ret:
return dwRet;
}
// USEC -- (US Export Controls)
DWORD SetSaltForExportControl(
HCRYPTKEY hKey,
PBYTE pbSalt,
DWORD cbSalt)
{
DWORD dwRet;
DWORD cbAllowableSaltLen;
// first check and make sure we can set this salt
if (!CryptGetKeyParam(
hKey,
KP_SALT,
NULL,
&cbAllowableSaltLen,
0))
{
/*
dwRet = GetLastError();
goto Ret;
*/
// If cbSalt == 0, no error
if (cbSalt == 0)
dwRet = ERROR_SUCCESS;
else
dwRet = GetLastError();
goto Ret;
}
if (cbAllowableSaltLen != cbSalt)
{
dwRet = ERROR_INVALID_DATA;
goto Ret;
}
// don't set if salt zero len (bug workaround for NT5B1 RSAEnh)
if (cbSalt != 0)
{
// set the salt to stomp real key bits (export law)
if (!CryptSetKeyParam(
hKey,
KP_SALT,
pbSalt,
0))
{
dwRet = GetLastError();
goto Ret;
}
}
dwRet = ERROR_SUCCESS;
Ret:
return dwRet;
}
DWORD GetCryptProviderFromRequirements(
DWORD dwAlgId1,
DWORD* pdwKeySize1,
DWORD dwAlgId2,
DWORD* pdwKeySize2,
DWORD* pdwProvType,
LPWSTR* ppszProvName)
{
DWORD dwRet;
DWORD cbProvName=0, cbNecessary;
HCRYPTPROV hQueryProv = NULL;
LPWSTR pTemp;
SS_ASSERT(pdwKeySize1);
SS_ASSERT(pdwKeySize2);
*ppszProvName=NULL;
for (int iProvIndex=0; ;iProvIndex++)
{
if (!_CryptEnumProvidersW(
iProvIndex,
NULL,
0,
pdwProvType,
NULL,
&cbNecessary))
{
dwRet = GetLastError();
if (dwRet == ERROR_NO_MORE_ITEMS)
dwRet = NTE_PROV_DLL_NOT_FOUND;
// end of providers OR
// trouble enumerating providers: both fatal
goto Ret;
}
if (cbNecessary > cbProvName)
{
if (*ppszProvName == NULL)
*ppszProvName = (LPWSTR)SSAlloc(cbNecessary);
else {
pTemp = (LPWSTR)SSReAlloc(*ppszProvName, cbNecessary);
if (NULL == pTemp) {
SSFree(*ppszProvName);
}
*ppszProvName = pTemp;
}
if (*ppszProvName == NULL)
{
dwRet = ERROR_NOT_ENOUGH_MEMORY;
goto Ret;
}
cbProvName = cbNecessary;
}
if (!_CryptEnumProvidersW(
iProvIndex,
NULL,
0,
pdwProvType,
*ppszProvName,
&cbNecessary))
{
// trouble enumerating providers: fatal
dwRet = GetLastError();
goto Ret;
}
if (!CryptAcquireContextU(
&hQueryProv,
NULL,
*ppszProvName,
*pdwProvType,
CRYPT_VERIFYCONTEXT))
{
// trouble acquiring context, to go next csp
continue;
}
if ((FProviderSupportsAlg(hQueryProv, dwAlgId1, pdwKeySize1)) &&
(FProviderSupportsAlg(hQueryProv, dwAlgId2, pdwKeySize2)) )
goto CSPFound;
// release
CryptReleaseContext(hQueryProv, 0);
hQueryProv = NULL;
}
CSPFound:
dwRet = ERROR_SUCCESS;
Ret:
if (hQueryProv != NULL)
CryptReleaseContext(hQueryProv, 0);
if ((dwRet != ERROR_SUCCESS) && (*ppszProvName))
{
SSFree(*ppszProvName);
*ppszProvName = NULL;
}
return dwRet;
}
// *pdwKeySize == -1 gets any size, reports size
HCRYPTPROV
GetCryptProviderHandle(
DWORD dwDefaultCSPType,
DWORD dwAlgId1,
DWORD* pdwKeySize1,
DWORD dwAlgId2,
DWORD* pdwKeySize2)
{
DWORD dwRet;
DWORD dwProvType;
LPWSTR szProvName = NULL;
CRYPTPROV_LIST_ITEM Elt, *pFoundElt;
HCRYPTPROV hNewCryptProv=0;
SS_ASSERT(pdwKeySize1);
SS_ASSERT(pdwKeySize2);
if(NULL == g_pCProvList)
{
SetLastError(PST_E_FAIL);
return NULL;
}
// Adjust DES key sizes, to prevent the situation where the CSP
// originally used for encryption accidently reported a 3DES key
// size of 192 bits, and the current CSPs only enumerate support
// for 3DES keys of 168 bits.
if(dwAlgId1 == CALG_DES || dwAlgId1 == CALG_3DES)
{
*pdwKeySize1 = -1;
}
// check cache for satisfactory CSP
CreateCryptProvListItem(&Elt,
dwAlgId1,
*pdwKeySize1,
dwAlgId2,
*pdwKeySize2,
0);
if (NULL != (pFoundElt = g_pCProvList->SearchList(&Elt)) )
{
// report what we're returning
*pdwKeySize1 = pFoundElt->dwKeySize1;
*pdwKeySize2 = pFoundElt->dwKeySize2;
return pFoundElt->hProv;
}
// not in cache: have to rummage
// try default provider of given type, see if it satisfies
if (CryptAcquireContextU(
&hNewCryptProv,
NULL,
MS_STRONG_PROV,
dwDefaultCSPType,
CRYPT_VERIFYCONTEXT))
{
if ((FProviderSupportsAlg(hNewCryptProv, dwAlgId1, pdwKeySize1)) &&
(FProviderSupportsAlg(hNewCryptProv, dwAlgId2, pdwKeySize2)) )
goto CSPAcquired;
// clean up non-usable CSP
CryptReleaseContext(hNewCryptProv, 0);
hNewCryptProv = NULL;
// all other cases: fall through to enum CSPs
}
// rummage along providers on system to find someone who fits the bill
if(ERROR_SUCCESS != (dwRet =
GetCryptProviderFromRequirements(
dwAlgId1,
pdwKeySize1,
dwAlgId2,
pdwKeySize2,
&dwProvType,
&szProvName)))
{
SetLastError(dwRet);
goto Ret;
}
// found one!
// init csp
if (!CryptAcquireContextU(
&hNewCryptProv,
NULL,
szProvName,
dwProvType,
CRYPT_VERIFYCONTEXT))
{
// this a failure case
// SetLastError already done for us
hNewCryptProv = NULL;
goto Ret;
}
CSPAcquired:
SS_ASSERT(hNewCryptProv != NULL);
// and add to internal list
pFoundElt = (CRYPTPROV_LIST_ITEM*) SSAlloc(sizeof(CRYPTPROV_LIST_ITEM));
if(NULL == pFoundElt)
{
// clean up non-usable CSP
CryptReleaseContext(hNewCryptProv, 0);
hNewCryptProv = NULL;
goto Ret;
}
CreateCryptProvListItem(pFoundElt,
dwAlgId1,
*pdwKeySize1,
dwAlgId2,
*pdwKeySize2,
hNewCryptProv);
g_pCProvList->AddToList(pFoundElt);
Ret:
if (szProvName)
SSFree(szProvName);
return hNewCryptProv;
}
BOOL FProviderSupportsAlg(
HCRYPTPROV hQueryProv,
DWORD dwAlgId,
DWORD* pdwKeySize)
{
PROV_ENUMALGS sSupportedAlgs;
PROV_ENUMALGS_EX sSupportedAlgsEx;
DWORD cbSupportedAlgs = sizeof(sSupportedAlgs);
DWORD cbSupportedAlgsEx = sizeof(sSupportedAlgsEx);
// must be non-null
SS_ASSERT(pdwKeySize != NULL);
// now we have provider; enum the algorithms involved
for(int iAlgs=0; ; iAlgs++)
{
//
// Attempt the EX alg enumeration
if (CryptGetProvParam(
hQueryProv,
PP_ENUMALGS_EX,
(PBYTE)&sSupportedAlgsEx,
&cbSupportedAlgsEx,
(iAlgs == 0) ? CRYPT_FIRST : 0 ))
{
if (sSupportedAlgsEx.aiAlgid == dwAlgId)
{
if(*pdwKeySize == -1)
{
*pdwKeySize = sSupportedAlgsEx.dwMaxLen;
}
else
{
if ((sSupportedAlgsEx.dwMinLen > *pdwKeySize) ||
(sSupportedAlgsEx.dwMaxLen < *pdwKeySize))
return FALSE;
}
return TRUE;
}
}
else if (!CryptGetProvParam(
hQueryProv,
PP_ENUMALGS,
(PBYTE)&sSupportedAlgs,
&cbSupportedAlgs,
(iAlgs == 0) ? CRYPT_FIRST : 0 ))
{
// trouble enumerating algs
break;
if (sSupportedAlgs.aiAlgid == dwAlgId)
{
// were we told to ignore size?
if (*pdwKeySize != -1)
{
// else, if defaults don't match
if (sSupportedAlgs.dwBitLen != *pdwKeySize)
{
return FALSE;
}
}
// report back size
*pdwKeySize = sSupportedAlgs.dwBitLen;
return TRUE;
}
}
else
{
// trouble enumerating algs
break;
}
}
return FALSE;
}
BOOL
WINAPI
_CryptEnumProvidersW(
DWORD dwIndex,
DWORD * pdwReserved,
DWORD dwFlags,
DWORD * pdwProvType,
LPWSTR pszProvName,
DWORD * pcbProvName
)
{
HMODULE hAdvapi32;
typedef BOOL (WINAPI *CRYPTENUMPROVIDERSW)(
DWORD dwIndex,
DWORD * pdwReserved,
DWORD dwFlags,
DWORD * pdwProvType,
LPWSTR pszProvName,
DWORD * pcbProvName
);
static CRYPTENUMPROVIDERSW _RealCryptEnumProvidersW;
if (_RealCryptEnumProvidersW == NULL) {
hAdvapi32 = GetModuleHandleA("advapi32.dll");
if(hAdvapi32 == NULL)
return FALSE;
_RealCryptEnumProvidersW = (CRYPTENUMPROVIDERSW)GetProcAddress(hAdvapi32, "CryptEnumProvidersW");
if(_RealCryptEnumProvidersW == NULL)
return FALSE;
}
return _RealCryptEnumProvidersW(
dwIndex,
pdwReserved,
dwFlags,
pdwProvType,
pszProvName,
pcbProvName
);
}