Support for TLS1 pseudo random function.

Some helper functions for DATA_BLOB's.
Computing TLS1 master secret from the pre-master secret.
Deriving the master hash from the master secret.
Deriving TLS1 MAC and encryption keys from the master hash.
Tests for most of the above.
This commit is contained in:
Michael Jung 2005-01-24 12:50:29 +00:00 committed by Alexandre Julliard
parent bfef6208a2
commit fb85c9e924
2 changed files with 664 additions and 48 deletions

View File

@ -4,7 +4,7 @@
*
* Copyright 2002 TransGaming Technologies (David Hammerton)
* Copyright 2004 Mike McCormack for CodeWeavers
* Copyright 2004 Michael Jung
* Copyright 2004, 2005 Michael Jung
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -43,10 +43,16 @@ WINE_DEFAULT_DEBUG_CHANNEL(crypt);
* CRYPTHASH - hash objects
*/
#define RSAENH_MAGIC_HASH 0x85938417u
#define RSAENH_MAX_HASH_SIZE 36
#define RSAENH_MAX_HASH_SIZE 104
#define RSAENH_HASHSTATE_IDLE 0
#define RSAENH_HASHSTATE_HASHING 1
#define RSAENH_HASHSTATE_FINISHED 2
typedef struct _RSAENH_TLS1PRF_PARAMS
{
CRYPT_DATA_BLOB blobLabel;
CRYPT_DATA_BLOB blobSeed;
} RSAENH_TLS1PRF_PARAMS;
typedef struct tagCRYPTHASH
{
OBJECTHDR header;
@ -58,6 +64,7 @@ typedef struct tagCRYPTHASH
HASH_CONTEXT context;
BYTE abHashValue[RSAENH_MAX_HASH_SIZE];
PHMAC_INFO pHMACInfo;
RSAENH_TLS1PRF_PARAMS tpPRFParams;
} CRYPTHASH;
/******************************************************************************
@ -69,6 +76,15 @@ typedef struct tagCRYPTHASH
#define RSAENH_KEYSTATE_IDLE 0
#define RSAENH_KEYSTATE_ENCRYPTING 1
#define RSAENH_KEYSTATE_DECRYPTING 2
#define RSAENH_KEYSTATE_MASTERKEY 3
typedef struct _RSAENH_SCHANNEL_INFO
{
SCHANNEL_ALG saEncAlg;
SCHANNEL_ALG saMACAlg;
CRYPT_DATA_BLOB blobClientRandom;
CRYPT_DATA_BLOB blobServerRandom;
} RSAENH_SCHANNEL_INFO;
typedef struct tagCRYPTKEY
{
OBJECTHDR header;
@ -85,6 +101,7 @@ typedef struct tagCRYPTKEY
BYTE abKeyValue[RSAENH_MAX_KEY_SIZE];
BYTE abInitVector[RSAENH_MAX_BLOCK_SIZE];
BYTE abChainVector[RSAENH_MAX_BLOCK_SIZE];
RSAENH_SCHANNEL_INFO siSChannelInfo;
} CRYPTKEY;
/******************************************************************************
@ -135,7 +152,7 @@ typedef struct tagKEYCONTAINER
/******************************************************************************
* aProvEnumAlgsEx - Defines the capabilities of the CSP personalities.
*/
#define RSAENH_MAX_ENUMALGS 19
#define RSAENH_MAX_ENUMALGS 20
#define RSAENH_PCT1_SSL2_SSL3_TLS1 (CRYPT_FLAG_PCT1|CRYPT_FLAG_SSL2|CRYPT_FLAG_SSL3|CRYPT_FLAG_TLS1)
PROV_ENUMALGS_EX aProvEnumAlgsEx[4][RSAENH_MAX_ENUMALGS+1] =
{
@ -208,6 +225,7 @@ PROV_ENUMALGS_EX aProvEnumAlgsEx[4][RSAENH_MAX_ENUMALGS+1] =
{CALG_SCHANNEL_MASTER_HASH,0,0,-1,0, 16,"SCH MASTER HASH",21,"SChannel Master Hash"},
{CALG_SCHANNEL_MAC_KEY,0,0,-1,0, 12,"SCH MAC KEY",17,"SChannel MAC Key"},
{CALG_SCHANNEL_ENC_KEY,0,0,-1,0, 12,"SCH ENC KEY",24,"SChannel Encryption Key"},
{CALG_TLS1PRF, 0, 0, -1,0, 9,"TLS1 PRF", 28,"TLS1 Pseudo Random Function"},
{0, 0, 0, 0,0, 1,"", 1,""}
}
};
@ -237,6 +255,23 @@ RSAENH_CPEncrypt(
DWORD dwBufLen
);
BOOL WINAPI
RSAENH_CPCreateHash(
HCRYPTPROV hProv,
ALG_ID Algid,
HCRYPTKEY hKey,
DWORD dwFlags,
HCRYPTHASH *phHash
);
BOOL WINAPI
RSAENH_CPSetHashParam(
HCRYPTPROV hProv,
HCRYPTHASH hHash,
DWORD dwParam,
BYTE *pbData, DWORD dwFlags
);
BOOL WINAPI
RSAENH_CPGetHashParam(
HCRYPTPROV hProv,
@ -247,6 +282,12 @@ RSAENH_CPGetHashParam(
DWORD dwFlags
);
BOOL WINAPI
RSAENH_CPDestroyHash(
HCRYPTPROV hProv,
HCRYPTHASH hHash
);
BOOL WINAPI
RSAENH_CPExportKey(
HCRYPTPROV hProv,
@ -367,6 +408,84 @@ static inline const PROV_ENUMALGS_EX* get_algid_info(HCRYPTPROV hProv, ALG_ID al
return NULL;
}
/******************************************************************************
* copy_data_blob [Internal]
*
* deeply copies a DATA_BLOB
*
* PARAMS
* dst [O] That's where the blob will be copied to
* src [I] Source blob
*
* RETURNS
* Success: TRUE
* Failure: FALSE (GetLastError() == NTE_NO_MEMORY
*
* NOTES
* Use free_data_blob to release resources occupied by copy_data_blob.
*/
static inline BOOL copy_data_blob(PCRYPT_DATA_BLOB dst, CONST PCRYPT_DATA_BLOB src) {
dst->pbData = (BYTE*)HeapAlloc(GetProcessHeap(), 0, src->cbData);
if (!dst->pbData) {
SetLastError(NTE_NO_MEMORY);
return FALSE;
}
dst->cbData = src->cbData;
memcpy(dst->pbData, src->pbData, src->cbData);
return TRUE;
}
/******************************************************************************
* concat_data_blobs [Internal]
*
* Concatenates two blobs
*
* PARAMS
* dst [O] The new blob will be copied here
* src1 [I] Prefix blob
* src2 [I] Appendix blob
*
* RETURNS
* Success: TRUE
* Failure: FALSE (GetLastError() == NTE_NO_MEMORY)
*
* NOTES
* Release resources occupied by concat_data_blobs with free_data_blobs
*/
static inline BOOL concat_data_blobs(PCRYPT_DATA_BLOB dst, CONST PCRYPT_DATA_BLOB src1,
CONST PCRYPT_DATA_BLOB src2)
{
dst->cbData = src1->cbData + src2->cbData;
dst->pbData = (BYTE*)HeapAlloc(GetProcessHeap(), 0, dst->cbData);
if (!dst->pbData) {
SetLastError(NTE_NO_MEMORY);
return FALSE;
}
memcpy(dst->pbData, src1->pbData, src1->cbData);
memcpy(dst->pbData + src1->cbData, src2->pbData, src2->cbData);
return TRUE;
}
/******************************************************************************
* free_data_blob [Internal]
*
* releases resource occupied by a dynamically allocated CRYPT_DATA_BLOB
*
* PARAMS
* pBlob [I] Heap space occupied by pBlob->pbData is released
*/
static inline void free_data_blob(PCRYPT_DATA_BLOB pBlob) {
HeapFree(GetProcessHeap(), 0, pBlob->pbData);
}
/******************************************************************************
* init_data_blob [Internal]
*/
static inline void init_data_blob(PCRYPT_DATA_BLOB pBlob) {
pBlob->pbData = NULL;
pBlob->cbData = 0;
}
/******************************************************************************
* free_hmac_info [Internal]
*
@ -440,9 +559,13 @@ static BOOL copy_hmac_info(PHMAC_INFO *dst, PHMAC_INFO src) {
* pCryptHash [I] Pointer to the hash object to be destroyed.
* Will be invalid after function returns!
*/
static void destroy_hash(OBJECTHDR *pCryptHash)
static void destroy_hash(OBJECTHDR *pObject)
{
free_hmac_info(((CRYPTHASH*)pCryptHash)->pHMACInfo);
CRYPTHASH *pCryptHash = (CRYPTHASH*)pObject;
free_hmac_info(pCryptHash->pHMACInfo);
free_data_blob(&pCryptHash->tpPRFParams.blobLabel);
free_data_blob(&pCryptHash->tpPRFParams.blobSeed);
HeapFree(GetProcessHeap(), 0, pCryptHash);
}
@ -572,9 +695,13 @@ static inline void finalize_hash(CRYPTHASH *pCryptHash) {
* pCryptKey [I] Pointer to the key object to be destroyed.
* Will be invalid after function returns!
*/
static void destroy_key(OBJECTHDR *pCryptKey)
static void destroy_key(OBJECTHDR *pObject)
{
free_key_impl(((CRYPTKEY*)pCryptKey)->aiAlgid, &((CRYPTKEY*)pCryptKey)->context);
CRYPTKEY *pCryptKey = (CRYPTKEY*)pObject;
free_key_impl(pCryptKey->aiAlgid, &pCryptKey->context);
free_data_blob(&pCryptKey->siSChannelInfo.blobClientRandom);
free_data_blob(&pCryptKey->siSChannelInfo.blobServerRandom);
HeapFree(GetProcessHeap(), 0, pCryptKey);
}
@ -689,6 +816,8 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK
pCryptKey->dwSaltLen = 0;
memset(pCryptKey->abKeyValue, 0, sizeof(pCryptKey->abKeyValue));
memset(pCryptKey->abInitVector, 0, sizeof(pCryptKey->abInitVector));
init_data_blob(&pCryptKey->siSChannelInfo.blobClientRandom);
init_data_blob(&pCryptKey->siSChannelInfo.blobServerRandom);
switch(aiAlgid)
{
@ -1016,6 +1145,135 @@ static BOOL build_hash_signature(BYTE *pbSignature, DWORD dwLen, ALG_ID aiAlgid,
return TRUE;
}
/******************************************************************************
* tls1_p [Internal]
*
* This is an implementation of the 'P_hash' helper function for TLS1's PRF.
* It is used exclusively by tls1_prf. For details see RFC 2246, chapter 5.
* The pseudo random stream generated by this function is exclusive or'ed with
* the data in pbBuffer.
*
* PARAMS
* hHMAC [I] HMAC object, which will be used in pseudo random generation
* pblobSeed [I] Seed value
* pbBuffer [I/O] Pseudo random stream will be xor'ed to the provided data
* dwBufferLen [I] Number of pseudo random bytes desired
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
static BOOL tls1_p(HCRYPTHASH hHMAC, CONST PCRYPT_DATA_BLOB pblobSeed, PBYTE pbBuffer, DWORD dwBufferLen)
{
CRYPTHASH *pHMAC;
BYTE abAi[RSAENH_MAX_HASH_SIZE];
DWORD i = 0;
if (!lookup_handle(&handle_table, hHMAC, RSAENH_MAGIC_HASH, (OBJECTHDR**)&pHMAC)) {
SetLastError(NTE_BAD_HASH);
return FALSE;
}
/* compute A_1 = HMAC(seed) */
init_hash(pHMAC);
update_hash(pHMAC, pblobSeed->pbData, pblobSeed->cbData);
finalize_hash(pHMAC);
memcpy(abAi, pHMAC->abHashValue, pHMAC->dwHashSize);
do {
/* compute HMAC(A_i + seed) */
init_hash(pHMAC);
update_hash(pHMAC, abAi, pHMAC->dwHashSize);
update_hash(pHMAC, pblobSeed->pbData, pblobSeed->cbData);
finalize_hash(pHMAC);
/* pseudo random stream := CONCAT_{i=1..n} ( HMAC(A_i + seed) ) */
do {
if (i >= dwBufferLen) break;
pbBuffer[i] ^= pHMAC->abHashValue[i % pHMAC->dwHashSize];
i++;
} while (i % pHMAC->dwHashSize);
/* compute A_{i+1} = HMAC(A_i) */
init_hash(pHMAC);
update_hash(pHMAC, abAi, pHMAC->dwHashSize);
finalize_hash(pHMAC);
memcpy(abAi, pHMAC->abHashValue, pHMAC->dwHashSize);
} while (i < dwBufferLen);
return TRUE;
}
/******************************************************************************
* tls1_prf [Internal]
*
* TLS1 pseudo random function as specified in RFC 2246, chapter 5
*
* PARAMS
* hProv [I] Key container used to compute the pseudo random stream
* hSecret [I] Key that holds the (pre-)master secret
* pblobLabel [I] Descriptive label
* pblobSeed [I] Seed value
* pbBuffer [O] Pseudo random numbers will be stored here
* dwBufferLen [I] Number of pseudo random bytes desired
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
static BOOL tls1_prf(HCRYPTPROV hProv, HCRYPTPROV hSecret, CONST PCRYPT_DATA_BLOB pblobLabel,
CONST PCRYPT_DATA_BLOB pblobSeed, PBYTE pbBuffer, DWORD dwBufferLen)
{
HMAC_INFO hmacInfo = { 0, NULL, 0, NULL, 0 };
HCRYPTHASH hHMAC = (HCRYPTHASH)INVALID_HANDLE_VALUE;
HCRYPTKEY hHalfSecret = (HCRYPTKEY)INVALID_HANDLE_VALUE;
CRYPTKEY *pHalfSecret, *pSecret;
DWORD dwHalfSecretLen;
BOOL result = FALSE;
CRYPT_DATA_BLOB blobLabelSeed;
TRACE("(hProv=%08lx, hSecret=%08lx, pblobLabel=%p, pblobSeed=%p, pbBuffer=%p, dwBufferLen=%ld)\n",
hProv, hSecret, pblobLabel, pblobSeed, pbBuffer, dwBufferLen);
if (!lookup_handle(&handle_table, hSecret, RSAENH_MAGIC_KEY, (OBJECTHDR**)&pSecret)) {
SetLastError(NTE_FAIL);
return FALSE;
}
dwHalfSecretLen = (pSecret->dwKeyLen+1)/2;
/* concatenation of the label and the seed */
if (!concat_data_blobs(&blobLabelSeed, pblobLabel, pblobSeed)) goto exit;
/* zero out the buffer, since two random streams will be xor'ed into it. */
memset(pbBuffer, 0, dwBufferLen);
/* build a 'fake' key, to hold the secret. CALG_SSL2_MASTER is used since it provides
* the biggest range of valid key lengths. */
hHalfSecret = new_key(hProv, CALG_SSL2_MASTER, MAKELONG(0,dwHalfSecretLen*8), &pHalfSecret);
if (hHalfSecret == (HCRYPTKEY)INVALID_HANDLE_VALUE) goto exit;
/* Derive an HMAC_MD5 hash and call the helper function. */
memcpy(pHalfSecret->abKeyValue, pSecret->abKeyValue, dwHalfSecretLen);
if (!RSAENH_CPCreateHash(hProv, CALG_HMAC, hHalfSecret, 0, &hHMAC)) goto exit;
hmacInfo.HashAlgid = CALG_MD5;
if (!RSAENH_CPSetHashParam(hProv, hHMAC, HP_HMAC_INFO, (BYTE*)&hmacInfo, 0)) goto exit;
if (!tls1_p(hHMAC, &blobLabelSeed, pbBuffer, dwBufferLen)) goto exit;
/* Reconfigure to HMAC_SHA hash and call helper function again. */
memcpy(pHalfSecret->abKeyValue, pSecret->abKeyValue + (pSecret->dwKeyLen/2), dwHalfSecretLen);
hmacInfo.HashAlgid = CALG_SHA;
if (!RSAENH_CPSetHashParam(hProv, hHMAC, HP_HMAC_INFO, (BYTE*)&hmacInfo, 0)) goto exit;
if (!tls1_p(hHMAC, &blobLabelSeed, pbBuffer, dwBufferLen)) goto exit;
result = TRUE;
exit:
release_handle(&handle_table, hHalfSecret, RSAENH_MAGIC_KEY);
if (hHMAC != (HCRYPTHASH)INVALID_HANDLE_VALUE) RSAENH_CPDestroyHash(hProv, hHMAC);
free_data_blob(&blobLabelSeed);
return result;
}
/******************************************************************************
* CPAcquireContext (RSAENH.@)
*
@ -1135,6 +1393,7 @@ BOOL WINAPI RSAENH_CPAcquireContext(HCRYPTPROV *phProv, LPSTR pszContainer,
BOOL WINAPI RSAENH_CPCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags,
HCRYPTHASH *phHash)
{
CRYPTKEY *pCryptKey;
CRYPTHASH *pCryptHash;
const PROV_ENUMALGS_EX *peaAlgidInfo;
@ -1150,9 +1409,9 @@ BOOL WINAPI RSAENH_CPCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey,
return FALSE;
}
if ((Algid == CALG_MAC || Algid == CALG_HMAC)) {
CRYPTKEY *pCryptKey;
if (Algid == CALG_MAC || Algid == CALG_HMAC || Algid == CALG_SCHANNEL_MASTER_HASH ||
Algid == CALG_TLS1PRF)
{
if (!lookup_handle(&handle_table, hKey, RSAENH_MAGIC_KEY, (OBJECTHDR**)&pCryptKey)) {
SetLastError(NTE_BAD_KEY);
return FALSE;
@ -1162,6 +1421,18 @@ BOOL WINAPI RSAENH_CPCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey,
SetLastError(NTE_BAD_KEY);
return FALSE;
}
if ((Algid == CALG_SCHANNEL_MASTER_HASH || Algid == CALG_TLS1PRF) &&
(pCryptKey->aiAlgid != CALG_TLS1_MASTER))
{
SetLastError(NTE_BAD_KEY);
return FALSE;
}
if ((Algid == CALG_TLS1PRF) && (pCryptKey->dwState != RSAENH_KEYSTATE_MASTERKEY)) {
SetLastError(NTE_BAD_KEY_STATE);
return FALSE;
}
}
*phHash = (HCRYPTHASH)new_object(&handle_table, sizeof(CRYPTHASH), RSAENH_MAGIC_HASH,
@ -1174,7 +1445,41 @@ BOOL WINAPI RSAENH_CPCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey,
pCryptHash->dwState = RSAENH_HASHSTATE_IDLE;
pCryptHash->pHMACInfo = (PHMAC_INFO)NULL;
pCryptHash->dwHashSize = peaAlgidInfo->dwDefaultLen >> 3;
init_data_blob(&pCryptHash->tpPRFParams.blobLabel);
init_data_blob(&pCryptHash->tpPRFParams.blobSeed);
if (Algid == CALG_SCHANNEL_MASTER_HASH) {
CRYPT_DATA_BLOB blobRandom, blobKeyExpansion = { 13, "key expansion" };
if (pCryptKey->dwState != RSAENH_KEYSTATE_MASTERKEY) {
CRYPT_DATA_BLOB blobLabel = { 13, "master secret" };
BYTE abKeyValue[48];
/* See RFC 2246, chapter 8.1 */
if (!concat_data_blobs(&blobRandom,
&pCryptKey->siSChannelInfo.blobClientRandom,
&pCryptKey->siSChannelInfo.blobServerRandom))
{
return FALSE;
}
tls1_prf(hProv, hKey, &blobLabel, &blobRandom, abKeyValue, 48);
pCryptKey->dwState = RSAENH_KEYSTATE_MASTERKEY;
memcpy(pCryptKey->abKeyValue, abKeyValue, 48);
free_data_blob(&blobRandom);
}
/* See RFC 2246, chapter 6.3 */
if (!concat_data_blobs(&blobRandom,
&pCryptKey->siSChannelInfo.blobServerRandom,
&pCryptKey->siSChannelInfo.blobClientRandom))
{
return FALSE;
}
tls1_prf(hProv, hKey, &blobKeyExpansion, &blobRandom, pCryptHash->abHashValue,
RSAENH_MAX_HASH_SIZE);
free_data_blob(&blobRandom);
}
return init_hash(pCryptHash);
}
@ -1293,6 +1598,8 @@ BOOL WINAPI RSAENH_CPDuplicateHash(HCRYPTPROV hUID, HCRYPTHASH hHash, DWORD *pdw
memcpy(pDestHash, pSrcHash, sizeof(CRYPTHASH));
duplicate_hash_impl(pSrcHash->aiAlgid, &pSrcHash->context, &pDestHash->context);
copy_hmac_info(&pDestHash->pHMACInfo, pSrcHash->pHMACInfo);
copy_data_blob(&pDestHash->tpPRFParams.blobLabel, &pSrcHash->tpPRFParams.blobLabel);
copy_data_blob(&pDestHash->tpPRFParams.blobSeed, &pSrcHash->tpPRFParams.blobSeed);
}
return *phHash != (HCRYPTHASH)INVALID_HANDLE_VALUE;
@ -1345,6 +1652,10 @@ BOOL WINAPI RSAENH_CPDuplicateKey(HCRYPTPROV hUID, HCRYPTKEY hKey, DWORD *pdwRes
if (*phKey != (HCRYPTKEY)INVALID_HANDLE_VALUE)
{
memcpy(pDestKey, pSrcKey, sizeof(CRYPTKEY));
copy_data_blob(&pDestKey->siSChannelInfo.blobServerRandom,
&pSrcKey->siSChannelInfo.blobServerRandom);
copy_data_blob(&pDestKey->siSChannelInfo.blobClientRandom,
&pSrcKey->siSChannelInfo.blobClientRandom);
duplicate_key_impl(pSrcKey->aiAlgid, &pSrcKey->context, &pDestKey->context);
return TRUE;
}
@ -1537,7 +1848,7 @@ BOOL WINAPI RSAENH_CPDecrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash,
if (GET_ALG_CLASS(pCryptKey->aiAlgid) != ALG_CLASS_DATA_ENCRYPT) {
SetLastError(NTE_BAD_TYPE);
return FALSE;
}
}
if (pCryptKey->dwState == RSAENH_KEYSTATE_IDLE)
pCryptKey->dwState = RSAENH_KEYSTATE_DECRYPTING;
@ -2082,6 +2393,11 @@ BOOL WINAPI RSAENH_CPGetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwPa
sizeof(DWORD));
case HP_HASHVAL:
if (pCryptHash->aiAlgid == CALG_TLS1PRF) {
return tls1_prf(hProv, pCryptHash->hKey, &pCryptHash->tpPRFParams.blobLabel,
&pCryptHash->tpPRFParams.blobSeed, pbData, *pdwDataLen);
}
if (pCryptHash->dwState == RSAENH_HASHSTATE_IDLE) {
SetLastError(NTE_BAD_HASH_STATE);
return FALSE;
@ -2168,6 +2484,28 @@ BOOL WINAPI RSAENH_CPSetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam
memcpy(pCryptKey->abInitVector, pbData, pCryptKey->dwBlockLen);
return TRUE;
case KP_SCHANNEL_ALG:
switch (((PSCHANNEL_ALG)pbData)->dwUse) {
case SCHANNEL_ENC_KEY:
memcpy(&pCryptKey->siSChannelInfo.saEncAlg, pbData, sizeof(SCHANNEL_ALG));
break;
case SCHANNEL_MAC_KEY:
memcpy(&pCryptKey->siSChannelInfo.saMACAlg, pbData, sizeof(SCHANNEL_ALG));
break;
default:
SetLastError(NTE_FAIL); /* FIXME: error code */
return FALSE;
}
return TRUE;
case KP_CLIENT_RANDOM:
return copy_data_blob(&pCryptKey->siSChannelInfo.blobClientRandom, (PCRYPT_DATA_BLOB)pbData);
case KP_SERVER_RANDOM:
return copy_data_blob(&pCryptKey->siSChannelInfo.blobServerRandom, (PCRYPT_DATA_BLOB)pbData);
default:
SetLastError(NTE_BAD_TYPE);
return FALSE;
@ -2258,6 +2596,9 @@ BOOL WINAPI RSAENH_CPGetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam
return copy_param(pbData, pdwDataLen, (CONST BYTE*)&pCryptKey->dwPermissions,
sizeof(DWORD));
case KP_ALGID:
return copy_param(pbData, pdwDataLen, (CONST BYTE*)&pCryptKey->aiAlgid, sizeof(DWORD));
default:
SetLastError(NTE_BAD_TYPE);
return FALSE;
@ -2414,7 +2755,7 @@ BOOL WINAPI RSAENH_CPGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData,
BOOL WINAPI RSAENH_CPDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseData,
DWORD dwFlags, HCRYPTKEY *phKey)
{
CRYPTKEY *pCryptKey;
CRYPTKEY *pCryptKey, *pMasterKey;
CRYPTHASH *pCryptHash;
BYTE abHashValue[RSAENH_MAX_HASH_SIZE*2];
DWORD dwLen;
@ -2441,51 +2782,102 @@ BOOL WINAPI RSAENH_CPDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseD
return FALSE;
}
if (GET_ALG_CLASS(Algid) != ALG_CLASS_DATA_ENCRYPT)
switch (GET_ALG_CLASS(Algid))
{
SetLastError(NTE_BAD_KEY);
return FALSE;
}
*phKey = new_key(hProv, Algid, dwFlags, &pCryptKey);
if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE;
case ALG_CLASS_DATA_ENCRYPT:
*phKey = new_key(hProv, Algid, dwFlags, &pCryptKey);
if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE;
/*
* We derive the key material from the hash.
* If the hash value is not large enough for the claimed key, we have to construct
* a larger binary value based on the hash. This is documented in MSDN: CryptDeriveKey.
*/
dwLen = RSAENH_MAX_HASH_SIZE;
RSAENH_CPGetHashParam(pCryptHash->hProv, hBaseData, HP_HASHVAL, abHashValue, &dwLen, 0);
/*
* We derive the key material from the hash.
* If the hash value is not large enough for the claimed key, we have to construct
* a larger binary value based on the hash. This is documented in MSDN: CryptDeriveKey.
*/
dwLen = RSAENH_MAX_HASH_SIZE;
RSAENH_CPGetHashParam(pCryptHash->hProv, hBaseData, HP_HASHVAL, abHashValue, &dwLen, 0);
if (dwLen < pCryptKey->dwKeyLen) {
BYTE pad1[RSAENH_HMAC_DEF_PAD_LEN], pad2[RSAENH_HMAC_DEF_PAD_LEN], old_hashval[RSAENH_MAX_HASH_SIZE];
DWORD i;
if (dwLen < pCryptKey->dwKeyLen) {
BYTE pad1[RSAENH_HMAC_DEF_PAD_LEN], pad2[RSAENH_HMAC_DEF_PAD_LEN];
BYTE old_hashval[RSAENH_MAX_HASH_SIZE];
DWORD i;
memcpy(old_hashval, pCryptHash->abHashValue, RSAENH_MAX_HASH_SIZE);
memcpy(old_hashval, pCryptHash->abHashValue, RSAENH_MAX_HASH_SIZE);
for (i=0; i<RSAENH_HMAC_DEF_PAD_LEN; i++) {
pad1[i] = RSAENH_HMAC_DEF_IPAD_CHAR ^ (i<dwLen ? abHashValue[i] : 0);
pad2[i] = RSAENH_HMAC_DEF_OPAD_CHAR ^ (i<dwLen ? abHashValue[i] : 0);
}
for (i=0; i<RSAENH_HMAC_DEF_PAD_LEN; i++) {
pad1[i] = RSAENH_HMAC_DEF_IPAD_CHAR ^ (i<dwLen ? abHashValue[i] : 0);
pad2[i] = RSAENH_HMAC_DEF_OPAD_CHAR ^ (i<dwLen ? abHashValue[i] : 0);
}
init_hash(pCryptHash);
update_hash(pCryptHash, pad1, RSAENH_HMAC_DEF_PAD_LEN);
finalize_hash(pCryptHash);
memcpy(abHashValue, pCryptHash->abHashValue, pCryptHash->dwHashSize);
init_hash(pCryptHash);
update_hash(pCryptHash, pad1, RSAENH_HMAC_DEF_PAD_LEN);
finalize_hash(pCryptHash);
memcpy(abHashValue, pCryptHash->abHashValue, pCryptHash->dwHashSize);
init_hash(pCryptHash);
update_hash(pCryptHash, pad2, RSAENH_HMAC_DEF_PAD_LEN);
finalize_hash(pCryptHash);
memcpy(abHashValue+pCryptHash->dwHashSize, pCryptHash->abHashValue,
pCryptHash->dwHashSize);
init_hash(pCryptHash);
update_hash(pCryptHash, pad2, RSAENH_HMAC_DEF_PAD_LEN);
finalize_hash(pCryptHash);
memcpy(abHashValue+pCryptHash->dwHashSize, pCryptHash->abHashValue,
pCryptHash->dwHashSize);
memcpy(pCryptHash->abHashValue, old_hashval, RSAENH_MAX_HASH_SIZE);
}
memcpy(pCryptHash->abHashValue, old_hashval, RSAENH_MAX_HASH_SIZE);
}
memcpy(pCryptKey->abKeyValue, abHashValue,
RSAENH_MIN(pCryptKey->dwKeyLen, sizeof(pCryptKey->abKeyValue)));
memcpy(pCryptKey->abKeyValue, abHashValue,
RSAENH_MIN(pCryptKey->dwKeyLen, sizeof(pCryptKey->abKeyValue)));
break;
case ALG_CLASS_MSG_ENCRYPT:
if (!lookup_handle(&handle_table, pCryptHash->hKey, RSAENH_MAGIC_KEY,
(OBJECTHDR**)&pMasterKey))
{
SetLastError(NTE_FAIL); /* FIXME error code */
return FALSE;
}
switch (Algid)
{
/* See RFC 2246, chapter 6.3 Key calculation */
case CALG_SCHANNEL_ENC_KEY:
*phKey = new_key(hProv, pMasterKey->siSChannelInfo.saEncAlg.Algid,
MAKELONG(LOWORD(dwFlags),pMasterKey->siSChannelInfo.saEncAlg.cBits),
&pCryptKey);
if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE;
memcpy(pCryptKey->abKeyValue,
pCryptHash->abHashValue + (
2 * (pMasterKey->siSChannelInfo.saMACAlg.cBits / 8) +
((dwFlags & CRYPT_SERVER) ?
(pMasterKey->siSChannelInfo.saEncAlg.cBits / 8) : 0)),
pMasterKey->siSChannelInfo.saEncAlg.cBits / 8);
memcpy(pCryptKey->abInitVector,
pCryptHash->abHashValue + (
2 * (pMasterKey->siSChannelInfo.saMACAlg.cBits / 8) +
2 * (pMasterKey->siSChannelInfo.saEncAlg.cBits / 8) +
((dwFlags & CRYPT_SERVER) ? pCryptKey->dwBlockLen : 0)),
pCryptKey->dwBlockLen);
break;
case CALG_SCHANNEL_MAC_KEY:
*phKey = new_key(hProv, Algid,
MAKELONG(LOWORD(dwFlags),pMasterKey->siSChannelInfo.saMACAlg.cBits),
&pCryptKey);
if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE;
memcpy(pCryptKey->abKeyValue,
pCryptHash->abHashValue + ((dwFlags & CRYPT_SERVER) ?
pMasterKey->siSChannelInfo.saMACAlg.cBits / 8 : 0),
pMasterKey->siSChannelInfo.saMACAlg.cBits / 8);
break;
default:
SetLastError(NTE_BAD_ALGID);
return FALSE;
}
break;
default:
SetLastError(NTE_BAD_ALGID);
return FALSE;
}
setup_key(pCryptKey);
return TRUE;
}
@ -2762,6 +3154,12 @@ BOOL WINAPI RSAENH_CPSetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwPa
memcpy(pCryptHash->abHashValue, pbData, pCryptHash->dwHashSize);
pCryptHash->dwState = RSAENH_HASHSTATE_FINISHED;
return TRUE;
case HP_TLS1PRF_SEED:
return copy_data_blob(&pCryptHash->tpPRFParams.blobSeed, (PCRYPT_DATA_BLOB)pbData);
case HP_TLS1PRF_LABEL:
return copy_data_blob(&pCryptHash->tpPRFParams.blobLabel, (PCRYPT_DATA_BLOB)pbData);
default:
SetLastError(NTE_BAD_TYPE);

View File

@ -1085,6 +1085,223 @@ static void test_verify_signature() {
if (!result) return;
}
void test_schannel_provider()
{
HCRYPTPROV hProv;
HCRYPTKEY hRSAKey, hMasterSecret, hServerWriteKey;
HCRYPTHASH hMasterHash, hTLS1PRF;
BOOL result;
DWORD dwLen;
SCHANNEL_ALG saSChannelAlg;
CRYPT_DATA_BLOB data_blob;
BYTE abPlainPrivateKey[596] = {
0x07, 0x02, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00,
0x52, 0x53, 0x41, 0x32, 0x00, 0x04, 0x00, 0x00,
0x01, 0x00, 0x01, 0x00, 0x9b, 0x64, 0xef, 0xce,
0x31, 0x7c, 0xad, 0x56, 0xe2, 0x1e, 0x9b, 0x96,
0xb3, 0xf0, 0x29, 0x88, 0x6e, 0xa8, 0xc2, 0x11,
0x33, 0xd6, 0xcc, 0x8c, 0x69, 0xb2, 0x1a, 0xfd,
0xfc, 0x23, 0x21, 0x30, 0x4d, 0x29, 0x45, 0xb6,
0x3a, 0x67, 0x11, 0x80, 0x1a, 0x91, 0xf2, 0x9f,
0x01, 0xac, 0xc0, 0x11, 0x50, 0x5f, 0xcd, 0xb9,
0xad, 0x76, 0x9f, 0x6e, 0x91, 0x55, 0x71, 0xda,
0x97, 0x96, 0x96, 0x22, 0x75, 0xb4, 0x83, 0x44,
0x89, 0x9e, 0xf8, 0x44, 0x40, 0x7c, 0xd6, 0xcd,
0x9d, 0x88, 0xd6, 0x88, 0xbc, 0x56, 0xb7, 0x64,
0xe9, 0x2c, 0x24, 0x2f, 0x0d, 0x78, 0x55, 0x1c,
0xb2, 0x67, 0xb1, 0x5e, 0xbc, 0x0c, 0xcf, 0x1c,
0xe9, 0xd3, 0x9e, 0xa2, 0x15, 0x24, 0x73, 0xd6,
0xdb, 0x6f, 0x83, 0xb2, 0xf8, 0xbc, 0xe7, 0x47,
0x3b, 0x01, 0xef, 0x49, 0x08, 0x98, 0xd6, 0xa3,
0xf9, 0x25, 0x57, 0xe9, 0x39, 0x3c, 0x53, 0x30,
0x1b, 0xf2, 0xc9, 0x62, 0x31, 0x43, 0x5d, 0x84,
0x24, 0x30, 0x21, 0x9a, 0xad, 0xdb, 0x62, 0x91,
0xc8, 0x07, 0xd9, 0x2f, 0xd6, 0xb5, 0x37, 0x6f,
0xfe, 0x7a, 0x12, 0xbc, 0xd9, 0xd2, 0x2b, 0xbf,
0xd7, 0xb1, 0xfa, 0x7d, 0xc0, 0x48, 0xdd, 0x74,
0xdd, 0x55, 0x04, 0xa1, 0x8b, 0xc1, 0x0a, 0xc4,
0xa5, 0x57, 0x62, 0xee, 0x08, 0x8b, 0xf9, 0x19,
0x6c, 0x52, 0x06, 0xf8, 0x73, 0x0f, 0x24, 0xc9,
0x71, 0x9f, 0xc5, 0x45, 0x17, 0x3e, 0xae, 0x06,
0x81, 0xa2, 0x96, 0x40, 0x06, 0xbf, 0xeb, 0x9e,
0x80, 0x2b, 0x27, 0x20, 0x8f, 0x38, 0xcf, 0xeb,
0xff, 0x3b, 0x38, 0x41, 0x35, 0x69, 0x66, 0x13,
0x1d, 0x3c, 0x01, 0x3b, 0xf6, 0x37, 0xca, 0x9c,
0x61, 0x74, 0x98, 0xcf, 0xc9, 0x6e, 0xe8, 0x90,
0xc7, 0xb7, 0x33, 0xc0, 0x07, 0x3c, 0xf8, 0xc8,
0xf6, 0xf2, 0xd7, 0xf0, 0x21, 0x62, 0x58, 0x8a,
0x55, 0xbf, 0xa1, 0x2d, 0x3d, 0xa6, 0x69, 0xc5,
0x02, 0x19, 0x31, 0xf0, 0x94, 0x0f, 0x45, 0x5c,
0x95, 0x1b, 0x53, 0xbc, 0xf5, 0xb0, 0x1a, 0x8f,
0xbf, 0x40, 0xe0, 0xc7, 0x73, 0xe7, 0x72, 0x6e,
0xeb, 0xb1, 0x0f, 0x38, 0xc5, 0xf8, 0xee, 0x04,
0xed, 0x34, 0x1a, 0x10, 0xf9, 0x53, 0x34, 0xf3,
0x3e, 0xe6, 0x5c, 0xd1, 0x47, 0x65, 0xcd, 0xbd,
0xf1, 0x06, 0xcb, 0xb4, 0xb1, 0x26, 0x39, 0x9f,
0x71, 0xfe, 0x3d, 0xf8, 0x62, 0xab, 0x22, 0x8b,
0x0e, 0xdc, 0xb9, 0xe8, 0x74, 0x06, 0xfc, 0x8c,
0x25, 0xa1, 0xa9, 0xcf, 0x07, 0xf9, 0xac, 0x21,
0x01, 0x7b, 0x1c, 0xdc, 0x94, 0xbd, 0x47, 0xe1,
0xa0, 0x86, 0x59, 0x35, 0x6a, 0x6f, 0xb9, 0x70,
0x26, 0x7c, 0x3c, 0xfd, 0xbd, 0x81, 0x39, 0x36,
0x42, 0xc2, 0xbd, 0xbe, 0x84, 0x27, 0x9a, 0x69,
0x81, 0xda, 0x99, 0x27, 0xc2, 0x4f, 0x62, 0x33,
0xf4, 0x79, 0x30, 0xc5, 0x63, 0x54, 0x71, 0xf1,
0x47, 0x22, 0x25, 0x9b, 0x6c, 0x00, 0x2f, 0x1c,
0xf4, 0x1f, 0x85, 0xbc, 0xf6, 0x67, 0x6a, 0xe3,
0xf6, 0x55, 0x8a, 0xef, 0xd0, 0x0b, 0xd3, 0xa2,
0xc5, 0x51, 0x70, 0x15, 0x0a, 0xf0, 0x98, 0x4c,
0xb7, 0x19, 0x62, 0x0e, 0x2d, 0x2a, 0x4a, 0x7d,
0x7a, 0x0a, 0xc4, 0x17, 0xe3, 0x5d, 0x20, 0x52,
0xa9, 0x98, 0xc3, 0xaa, 0x11, 0xf6, 0xbf, 0x4c,
0x94, 0x99, 0x81, 0x89, 0xf0, 0x7f, 0x66, 0xaa,
0xc8, 0x88, 0xd7, 0x31, 0x84, 0x71, 0xb6, 0x64,
0x09, 0x76, 0x0b, 0x7f, 0x1a, 0x1f, 0x2e, 0xfe,
0xcd, 0x59, 0x2a, 0x54, 0x11, 0x84, 0xd4, 0x6a,
0x61, 0xdf, 0xaa, 0x76, 0x66, 0x9d, 0x82, 0x11,
0x56, 0x3d, 0xd2, 0x52, 0xe6, 0x42, 0x5a, 0x77,
0x92, 0x98, 0x34, 0xf3, 0x56, 0x6c, 0x96, 0x10,
0x40, 0x59, 0x16, 0xcb, 0x77, 0x61, 0xe3, 0xbf,
0x4b, 0xd4, 0x39, 0xfb, 0xb1, 0x4e, 0xc1, 0x74,
0xec, 0x7a, 0xea, 0x3d, 0x68, 0xbb, 0x0b, 0xe6,
0xc6, 0x06, 0xbf, 0xdd, 0x7f, 0x94, 0x42, 0xc0,
0x0f, 0xe4, 0x92, 0x33, 0x6c, 0x6e, 0x1b, 0xba,
0x73, 0xf9, 0x79, 0x84, 0xdf, 0x45, 0x00, 0xe4,
0x94, 0x88, 0x9d, 0x08, 0x89, 0xcf, 0xf2, 0xa4,
0xc5, 0x47, 0x45, 0x85, 0x86, 0xa5, 0xcc, 0xa8,
0xf2, 0x5d, 0x58, 0x07
};
BYTE abTLS1Master[140] = {
0x01, 0x02, 0x00, 0x00, 0x06, 0x4c, 0x00, 0x00,
0x00, 0xa4, 0x00, 0x00, 0x5b, 0x13, 0xc7, 0x68,
0xd8, 0x55, 0x23, 0x5d, 0xbc, 0xa6, 0x9d, 0x97,
0x0e, 0xcd, 0x6b, 0xcf, 0xc0, 0xdc, 0xc5, 0x53,
0x28, 0xa0, 0xca, 0xc1, 0x63, 0x4e, 0x3a, 0x24,
0x22, 0xe5, 0x4d, 0x15, 0xbb, 0xa5, 0x06, 0xc3,
0x98, 0x25, 0xdc, 0x35, 0xd3, 0xdb, 0xab, 0xb8,
0x44, 0x1b, 0xfe, 0x63, 0x88, 0x7c, 0x2e, 0x6d,
0x34, 0xd9, 0x0f, 0x7e, 0x2f, 0xc2, 0xb2, 0x6e,
0x56, 0xfa, 0xab, 0xb2, 0x88, 0xf6, 0x15, 0x6e,
0xa8, 0xcd, 0x70, 0x16, 0x94, 0x61, 0x07, 0x40,
0x9e, 0x25, 0x22, 0xf8, 0x64, 0x9f, 0xcc, 0x0b,
0xf1, 0x92, 0x4d, 0xfe, 0xc3, 0x5d, 0x52, 0xdb,
0x0f, 0xff, 0x12, 0x0f, 0x49, 0x43, 0x7d, 0xc6,
0x52, 0x61, 0xb0, 0x06, 0xc8, 0x1b, 0x90, 0xac,
0x09, 0x7e, 0x4b, 0x95, 0x69, 0x3b, 0x0d, 0x41,
0x1b, 0x4c, 0x65, 0x75, 0x4d, 0x85, 0x16, 0xc4,
0xd3, 0x1e, 0x82, 0xb3
};
BYTE abServerSecret[33] = "Super Secret Server Secret 12345";
BYTE abClientSecret[33] = "Super Secret Client Secret 12345";
BYTE abHashedHandshakes[37] = "123456789012345678901234567890123456";
BYTE abClientFinished[16] = "client finished";
BYTE abData[16] = "Wine rocks!";
static const BYTE abEncryptedData[16] = {
0x13, 0xd2, 0xdd, 0xeb, 0x6c, 0x3f, 0xbe, 0xb2,
0x04, 0x86, 0xb5, 0xe5, 0x08, 0xe5, 0xf3, 0x0d
};
static const BYTE abPRF[16] = {
0xa8, 0xb2, 0xa6, 0xef, 0x83, 0x4e, 0x74, 0xb1,
0xf3, 0xb1, 0x51, 0x5a, 0x1a, 0x2b, 0x11, 0x31
};
result = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_SCHANNEL, CRYPT_VERIFYCONTEXT);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
/* To get deterministic results, we import the TLS1 master secret (which
* is typically generated from a random generator). Therefore, we need
* an RSA key. */
dwLen = (DWORD)sizeof(abPlainPrivateKey);
result = CryptImportKey(hProv, abPlainPrivateKey, dwLen, 0, 0, &hRSAKey);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
dwLen = (DWORD)sizeof(abTLS1Master);
result = CryptImportKey(hProv, abTLS1Master, dwLen, hRSAKey, 0, &hMasterSecret);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
/* Setting the TLS1 client and server random parameters, as well as the
* MAC and encryption algorithm parameters. */
data_blob.cbData = 33;
data_blob.pbData = abClientSecret;
result = CryptSetKeyParam(hMasterSecret, KP_CLIENT_RANDOM, (BYTE*)&data_blob, 0);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
data_blob.cbData = 33;
data_blob.pbData = abServerSecret;
result = CryptSetKeyParam(hMasterSecret, KP_SERVER_RANDOM, (BYTE*)&data_blob, 0);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
saSChannelAlg.dwUse = SCHANNEL_ENC_KEY;
saSChannelAlg.Algid = CALG_DES;
saSChannelAlg.cBits = 64;
saSChannelAlg.dwFlags = 0;
saSChannelAlg.dwReserved = 0;
result = CryptSetKeyParam(hMasterSecret, KP_SCHANNEL_ALG, (PBYTE)&saSChannelAlg, 0);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
saSChannelAlg.dwUse = SCHANNEL_MAC_KEY;
saSChannelAlg.Algid = CALG_MD5;
saSChannelAlg.cBits = 128;
saSChannelAlg.dwFlags = 0;
saSChannelAlg.dwReserved = 0;
result = CryptSetKeyParam(hMasterSecret, KP_SCHANNEL_ALG, (PBYTE)&saSChannelAlg, 0);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
/* Deriving a hash from the master secret. This is due to the CryptoAPI architecture.
* (Keys can only be derived from hashes, not from other keys.) */
result = CryptCreateHash(hProv, CALG_SCHANNEL_MASTER_HASH, hMasterSecret, 0, &hMasterHash);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
/* Deriving the server write encryption key from the master hash */
result = CryptDeriveKey(hProv, CALG_SCHANNEL_ENC_KEY, hMasterHash, CRYPT_SERVER, &hServerWriteKey);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
/* Encrypting some data with the server write encryption key and checking the result. */
dwLen = 12;
result = CryptEncrypt(hServerWriteKey, 0, TRUE, 0, abData, &dwLen, 16);
ok (result && (dwLen == 16) && !memcmp(abData, abEncryptedData, 16), "%08lx\n", GetLastError());
/* Second test case: Test the TLS1 pseudo random number function. */
result = CryptCreateHash(hProv, CALG_TLS1PRF, hMasterSecret, 0, &hTLS1PRF);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
/* Set the label and seed parameters for the random number function */
data_blob.cbData = 36;
data_blob.pbData = abHashedHandshakes;
result = CryptSetHashParam(hTLS1PRF, HP_TLS1PRF_SEED, (BYTE*)&data_blob, 0);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
data_blob.cbData = 15;
data_blob.pbData = abClientFinished;
result = CryptSetHashParam(hTLS1PRF, HP_TLS1PRF_LABEL, (BYTE*)&data_blob, 0);
ok (result, "%08lx\n", GetLastError());
if (!result) return;
/* Generate some pseudo random bytes and check if they are correct. */
dwLen = (DWORD)sizeof(abData);
result = CryptGetHashParam(hTLS1PRF, HP_HASHVAL, abData, &dwLen, 0);
ok (result && (dwLen==(DWORD)sizeof(abData)) && !memcmp(abData, abPRF, sizeof(abData)),
"%08lx\n", GetLastError());
CryptDestroyHash(hTLS1PRF);
CryptDestroyHash(hMasterHash);
CryptDestroyKey(hServerWriteKey);
CryptDestroyKey(hRSAKey);
CryptDestroyKey(hMasterSecret);
CryptReleaseContext(hProv, 0);
}
START_TEST(rsaenh)
{
if (!init_environment())
@ -1103,4 +1320,5 @@ START_TEST(rsaenh)
test_import_private();
test_verify_signature();
clean_up_environment();
test_schannel_provider();
}