1950 lines
58 KiB
C
1950 lines
58 KiB
C
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1992 - 1995.
|
||
|
//
|
||
|
// File: keyxmspk.c
|
||
|
//
|
||
|
// Contents:
|
||
|
//
|
||
|
// Classes:
|
||
|
//
|
||
|
// Functions:
|
||
|
//
|
||
|
// History: 09-23-97 jbanes LSA integration stuff.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include <spbase.h>
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
#endif
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// PROV_RSA_SCHANNEL handle used when building ClientHello messages.
|
||
|
HCRYPTPROV g_hRsaSchannel = 0;
|
||
|
PROV_ENUMALGS_EX * g_pRsaSchannelAlgs = NULL;
|
||
|
DWORD g_cRsaSchannelAlgs = 0;
|
||
|
|
||
|
SP_STATUS
|
||
|
Ssl3ParseServerKeyExchange(
|
||
|
PSPContext pContext,
|
||
|
PBYTE pbMessage,
|
||
|
DWORD cbMessage,
|
||
|
HCRYPTKEY hServerPublic,
|
||
|
HCRYPTKEY *phNewServerPublic);
|
||
|
|
||
|
SP_STATUS
|
||
|
PkcsFinishMasterKey(
|
||
|
PSPContext pContext,
|
||
|
HCRYPTKEY hMasterKey);
|
||
|
|
||
|
SP_STATUS
|
||
|
WINAPI
|
||
|
PkcsGenerateServerExchangeValue(
|
||
|
SPContext * pContext, // in
|
||
|
PUCHAR pServerExchangeValue, // out
|
||
|
DWORD * pcbServerExchangeValue // in/out
|
||
|
);
|
||
|
|
||
|
|
||
|
SP_STATUS
|
||
|
WINAPI
|
||
|
PkcsGenerateClientExchangeValue(
|
||
|
SPContext * pContext, // in
|
||
|
PUCHAR pServerExchangeValue, // in
|
||
|
DWORD cbServerExchangeValue, // in
|
||
|
PUCHAR pClientClearValue, // out
|
||
|
DWORD * pcbClientClearValue, // in/out
|
||
|
PUCHAR pClientExchangeValue, // out
|
||
|
DWORD * pcbClientExchangeValue // in/out
|
||
|
);
|
||
|
|
||
|
SP_STATUS
|
||
|
WINAPI
|
||
|
PkcsGenerateServerMasterKey(
|
||
|
SPContext * pContext, // in
|
||
|
PUCHAR pClientClearValue, // in
|
||
|
DWORD cbClientClearValue, // in
|
||
|
PUCHAR pClientExchangeValue, // in
|
||
|
DWORD cbClientExchangeValue // in
|
||
|
);
|
||
|
|
||
|
|
||
|
KeyExchangeSystem keyexchPKCS = {
|
||
|
SP_EXCH_RSA_PKCS1,
|
||
|
"RSA",
|
||
|
// PkcsPrivateFromBlob,
|
||
|
PkcsGenerateServerExchangeValue,
|
||
|
PkcsGenerateClientExchangeValue,
|
||
|
PkcsGenerateServerMasterKey,
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
ReverseMemCopy(
|
||
|
PUCHAR Dest,
|
||
|
PUCHAR Source,
|
||
|
ULONG Size)
|
||
|
{
|
||
|
PUCHAR p;
|
||
|
|
||
|
p = Dest + Size - 1;
|
||
|
do
|
||
|
{
|
||
|
*p-- = *Source++;
|
||
|
} while (p >= Dest);
|
||
|
}
|
||
|
|
||
|
SP_STATUS
|
||
|
GenerateSsl3KeyPair(
|
||
|
PSPContext pContext, // in
|
||
|
DWORD dwKeySize, // in
|
||
|
HCRYPTPROV *phEphemeralProv, // out
|
||
|
HCRYPTKEY * phEphemeralKey) // out
|
||
|
{
|
||
|
HCRYPTPROV * phEphemProv;
|
||
|
PCRYPT_KEY_PROV_INFO pProvInfo = NULL;
|
||
|
PSPCredentialGroup pCredGroup;
|
||
|
PSPCredential pCred;
|
||
|
DWORD cbSize;
|
||
|
SP_STATUS pctRet;
|
||
|
|
||
|
pCredGroup = pContext->RipeZombie->pServerCred;
|
||
|
if(pCredGroup == NULL)
|
||
|
{
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
LockCredential(pCredGroup);
|
||
|
|
||
|
pCred = pContext->RipeZombie->pActiveServerCred;
|
||
|
if(pCred == NULL)
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if(dwKeySize == 512)
|
||
|
{
|
||
|
phEphemProv = &pCred->hEphem512Prov;
|
||
|
}
|
||
|
else if(dwKeySize == 1024)
|
||
|
{
|
||
|
phEphemProv = &pCred->hEphem1024Prov;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Obtain CSP context.
|
||
|
//
|
||
|
|
||
|
if(*phEphemProv == 0)
|
||
|
{
|
||
|
// Read the certificate context's "key info" property.
|
||
|
if(CertGetCertificateContextProperty(pCred->pCert,
|
||
|
CERT_KEY_PROV_INFO_PROP_ID,
|
||
|
NULL,
|
||
|
&cbSize))
|
||
|
{
|
||
|
pProvInfo = SPExternalAlloc(cbSize);
|
||
|
if(pProvInfo == NULL)
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if(!CertGetCertificateContextProperty(pCred->pCert,
|
||
|
CERT_KEY_PROV_INFO_PROP_ID,
|
||
|
pProvInfo,
|
||
|
&cbSize))
|
||
|
{
|
||
|
DebugLog((SP_LOG_ERROR, "Error 0x%x reading CERT_KEY_PROV_INFO_PROP_ID\n",GetLastError()));
|
||
|
SPExternalFree(pProvInfo);
|
||
|
pProvInfo = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Obtain a "verify only" csp context.
|
||
|
if(pProvInfo)
|
||
|
{
|
||
|
// If the private key belongs to one of the Microsoft PROV_RSA_FULL
|
||
|
// CSPs, then manually divert it to the Microsoft PROV_RSA_SCHANNEL
|
||
|
// CSP. This works because both CSP types use the same private key
|
||
|
// storage scheme.
|
||
|
if(pProvInfo->dwProvType == PROV_RSA_FULL)
|
||
|
{
|
||
|
if(lstrcmpW(pProvInfo->pwszProvName, MS_DEF_PROV_W) == 0 ||
|
||
|
lstrcmpW(pProvInfo->pwszProvName, MS_STRONG_PROV_W) == 0 ||
|
||
|
lstrcmpW(pProvInfo->pwszProvName, MS_ENHANCED_PROV_W) == 0)
|
||
|
{
|
||
|
DebugLog((DEB_WARN, "Force CSP type to PROV_RSA_SCHANNEL.\n"));
|
||
|
pProvInfo->pwszProvName = MS_DEF_RSA_SCHANNEL_PROV_W;
|
||
|
pProvInfo->dwProvType = PROV_RSA_SCHANNEL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!SchCryptAcquireContextW(phEphemProv,
|
||
|
NULL,
|
||
|
pProvInfo->pwszProvName,
|
||
|
pProvInfo->dwProvType,
|
||
|
CRYPT_VERIFYCONTEXT,
|
||
|
pCred->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pctRet = SEC_E_NO_CREDENTIALS;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
SPExternalFree(pProvInfo);
|
||
|
pProvInfo = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(!SchCryptAcquireContextW(phEphemProv,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
PROV_RSA_SCHANNEL,
|
||
|
CRYPT_VERIFYCONTEXT,
|
||
|
pCred->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pctRet = SEC_E_NO_CREDENTIALS;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Obtain handle to private key.
|
||
|
//
|
||
|
|
||
|
if(!SchCryptGetUserKey(*phEphemProv,
|
||
|
AT_KEYEXCHANGE,
|
||
|
phEphemeralKey,
|
||
|
pCred->dwCapiFlags))
|
||
|
{
|
||
|
// Key does not exist, so attempt to create one.
|
||
|
DebugLog((DEB_TRACE, "Creating %d-bit ephemeral key.\n", dwKeySize));
|
||
|
if(!SchCryptGenKey(*phEphemProv,
|
||
|
AT_KEYEXCHANGE,
|
||
|
(dwKeySize << 16),
|
||
|
phEphemeralKey,
|
||
|
pCred->dwCapiFlags))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR, "Error 0x%x generating ephemeral key\n", GetLastError()));
|
||
|
pctRet = SEC_E_NO_CREDENTIALS;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
DebugLog((DEB_TRACE, "Ephemeral key created okay.\n"));
|
||
|
}
|
||
|
|
||
|
|
||
|
*phEphemeralProv = *phEphemProv;
|
||
|
|
||
|
pctRet = PCT_ERR_OK;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if(pProvInfo)
|
||
|
{
|
||
|
SPExternalFree(pProvInfo);
|
||
|
}
|
||
|
|
||
|
UnlockCredential(pCredGroup);
|
||
|
|
||
|
return pctRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: PkcsGenerateServerExchangeValue
|
||
|
//
|
||
|
// Synopsis: Create a ServerKeyExchange message, containing an ephemeral
|
||
|
// RSA key.
|
||
|
//
|
||
|
// Arguments: [pContext] -- Schannel context.
|
||
|
// [pServerExchangeValue] --
|
||
|
// [pcbServerExchangeValue] --
|
||
|
//
|
||
|
// History: 10-09-97 jbanes Added CAPI integration.
|
||
|
//
|
||
|
// Notes: This routine is called by the server-side only.
|
||
|
//
|
||
|
// In the case of SSL3 or TLS, the ServerKeyExchange message
|
||
|
// consists of the following structure, signed with the
|
||
|
// server's private key.
|
||
|
//
|
||
|
// struct {
|
||
|
// opaque rsa_modulus<1..2^16-1>;
|
||
|
// opaque rsa_exponent<1..2^16-1>;
|
||
|
// } Server RSA Params;
|
||
|
//
|
||
|
// This message is only sent when the server's private key
|
||
|
// is greater then 512 bits and an export cipher suite is
|
||
|
// being negotiated.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
SP_STATUS
|
||
|
WINAPI
|
||
|
PkcsGenerateServerExchangeValue(
|
||
|
PSPContext pContext, // in
|
||
|
PBYTE pServerExchangeValue, // out
|
||
|
DWORD * pcbServerExchangeValue) // in/out
|
||
|
{
|
||
|
PSPCredential pCred;
|
||
|
HCRYPTKEY hServerKey;
|
||
|
HCRYPTPROV hEphemeralProv;
|
||
|
HCRYPTKEY hEphemeralKey;
|
||
|
DWORD cbData;
|
||
|
DWORD cbServerModulus;
|
||
|
PBYTE pbBlob = NULL;
|
||
|
DWORD cbBlob;
|
||
|
BLOBHEADER * pBlobHeader = NULL;
|
||
|
RSAPUBKEY * pRsaPubKey = NULL;
|
||
|
PBYTE pbModulus = NULL;
|
||
|
DWORD cbModulus;
|
||
|
DWORD cbExp;
|
||
|
PBYTE pbMessage = NULL;
|
||
|
DWORD cbSignature;
|
||
|
HCRYPTHASH hHash;
|
||
|
BYTE rgbHashValue[CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN];
|
||
|
UINT i;
|
||
|
SP_STATUS pctRet;
|
||
|
BOOL fImpersonating = FALSE;
|
||
|
UNICipherMap * pCipherSuite;
|
||
|
DWORD cbAllowedKeySize;
|
||
|
|
||
|
pCred = pContext->RipeZombie->pActiveServerCred;
|
||
|
if(pCred == NULL)
|
||
|
{
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
pContext->fExchKey = FALSE;
|
||
|
|
||
|
if(pContext->RipeZombie->fProtocol == SP_PROT_SSL2_SERVER ||
|
||
|
pContext->RipeZombie->fProtocol == SP_PROT_PCT1_SERVER)
|
||
|
{
|
||
|
// There is no ServerExchangeValue for SSL2 or PCT1
|
||
|
*pcbServerExchangeValue = 0;
|
||
|
return PCT_ERR_OK;
|
||
|
}
|
||
|
|
||
|
if(pContext->RipeZombie->fProtocol != SP_PROT_SSL3_SERVER &&
|
||
|
pContext->RipeZombie->fProtocol != SP_PROT_TLS1_SERVER)
|
||
|
{
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Determine if ServerKeyExchange message is necessary.
|
||
|
//
|
||
|
|
||
|
pCipherSuite = &UniAvailableCiphers[pContext->dwPendingCipherSuiteIndex];
|
||
|
|
||
|
if(pCipherSuite->dwFlags & DOMESTIC_CIPHER_SUITE)
|
||
|
{
|
||
|
// Message not necessary.
|
||
|
*pcbServerExchangeValue = 0;
|
||
|
return PCT_ERR_OK;
|
||
|
}
|
||
|
|
||
|
if(pCred->hProv == 0)
|
||
|
{
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
|
||
|
fImpersonating = SslImpersonateClient();
|
||
|
|
||
|
if(!SchCryptGetUserKey(pCred->hProv,
|
||
|
pCred->dwKeySpec,
|
||
|
&hServerKey,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR, "Error 0x%x obtaining handle to server public key\n",
|
||
|
GetLastError()));
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
cbData = sizeof(DWORD);
|
||
|
if(!SchCryptGetKeyParam(hServerKey,
|
||
|
KP_BLOCKLEN,
|
||
|
(PBYTE)&cbServerModulus,
|
||
|
&cbData,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
SchCryptDestroyKey(hServerKey, pContext->RipeZombie->dwCapiFlags);
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
SchCryptDestroyKey(hServerKey, pContext->RipeZombie->dwCapiFlags);
|
||
|
|
||
|
if(pCipherSuite->dwFlags & EXPORT56_CIPHER_SUITE)
|
||
|
{
|
||
|
cbAllowedKeySize = 1024;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cbAllowedKeySize = 512;
|
||
|
}
|
||
|
|
||
|
if(cbServerModulus <= cbAllowedKeySize)
|
||
|
{
|
||
|
// Message not necessary.
|
||
|
*pcbServerExchangeValue = 0;
|
||
|
pctRet = PCT_ERR_OK;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
// Convert size from bits to bytes.
|
||
|
cbServerModulus /= 8;
|
||
|
|
||
|
pContext->fExchKey = TRUE;
|
||
|
|
||
|
if(fImpersonating)
|
||
|
{
|
||
|
RevertToSelf();
|
||
|
fImpersonating = FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Compute approximate size of ServerKeyExchange message.
|
||
|
//
|
||
|
|
||
|
if(pServerExchangeValue == NULL)
|
||
|
{
|
||
|
*pcbServerExchangeValue =
|
||
|
2 + cbAllowedKeySize / 8 + // modulus
|
||
|
2 + sizeof(DWORD) + // exponent
|
||
|
2 + cbServerModulus; // signature
|
||
|
|
||
|
pctRet = PCT_ERR_OK;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Get handle to 512-bit ephemeral RSA key. Generate it if
|
||
|
// we haven't already.
|
||
|
//
|
||
|
|
||
|
pctRet = GenerateSsl3KeyPair(pContext,
|
||
|
cbAllowedKeySize,
|
||
|
&hEphemeralProv,
|
||
|
&hEphemeralKey);
|
||
|
if(pctRet != PCT_ERR_OK)
|
||
|
{
|
||
|
SP_LOG_RESULT(pctRet);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Export ephemeral key.
|
||
|
//
|
||
|
|
||
|
if(!SchCryptExportKey(hEphemeralKey,
|
||
|
0,
|
||
|
PUBLICKEYBLOB,
|
||
|
0,
|
||
|
NULL,
|
||
|
&cbBlob,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
pbBlob = SPExternalAlloc(cbBlob);
|
||
|
if(pbBlob == NULL)
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
if(!SchCryptExportKey(hEphemeralKey,
|
||
|
0,
|
||
|
PUBLICKEYBLOB,
|
||
|
0,
|
||
|
pbBlob,
|
||
|
&cbBlob,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
SPExternalFree(pbBlob);
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Destroy handle to ephemeral key. Don't release the ephemeral hProv
|
||
|
// though--that's owned by the credential.
|
||
|
SchCryptDestroyKey(hEphemeralKey, pContext->RipeZombie->dwCapiFlags);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Build message from key blob.
|
||
|
//
|
||
|
|
||
|
pBlobHeader = (BLOBHEADER *)pbBlob;
|
||
|
pRsaPubKey = (RSAPUBKEY *)(pBlobHeader + 1);
|
||
|
pbModulus = (BYTE *)(pRsaPubKey + 1);
|
||
|
cbModulus = pRsaPubKey->bitlen / 8;
|
||
|
|
||
|
pbMessage = pServerExchangeValue;
|
||
|
|
||
|
pbMessage[0] = MSBOF(cbModulus);
|
||
|
pbMessage[1] = LSBOF(cbModulus);
|
||
|
pbMessage += 2;
|
||
|
ReverseMemCopy(pbMessage, pbModulus, cbModulus);
|
||
|
pbMessage += cbModulus;
|
||
|
|
||
|
// Don't laugh, this works - pete
|
||
|
cbExp = ((pRsaPubKey->pubexp & 0xff000000) ? 4 :
|
||
|
((pRsaPubKey->pubexp & 0x00ff0000) ? 3 :
|
||
|
((pRsaPubKey->pubexp & 0x0000ff00) ? 2 : 1)));
|
||
|
pbMessage[0] = MSBOF(cbExp);
|
||
|
pbMessage[1] = LSBOF(cbExp);
|
||
|
pbMessage += 2;
|
||
|
ReverseMemCopy(pbMessage, (PBYTE)&pRsaPubKey->pubexp, cbExp);
|
||
|
pbMessage += cbExp;
|
||
|
|
||
|
SPExternalFree(pbBlob);
|
||
|
pbBlob = NULL;
|
||
|
|
||
|
fImpersonating = SslImpersonateClient();
|
||
|
|
||
|
// Generate hash values
|
||
|
ComputeServerExchangeHashes(
|
||
|
pContext,
|
||
|
pServerExchangeValue,
|
||
|
(int)(pbMessage - pServerExchangeValue),
|
||
|
rgbHashValue,
|
||
|
rgbHashValue + CB_MD5_DIGEST_LEN);
|
||
|
|
||
|
// Sign hash value.
|
||
|
if(!SchCryptCreateHash(pCred->hProv,
|
||
|
CALG_SSL3_SHAMD5,
|
||
|
0,
|
||
|
0,
|
||
|
&hHash,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
if(!SchCryptSetHashParam(hHash,
|
||
|
HP_HASHVAL,
|
||
|
rgbHashValue,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
DebugLog((DEB_TRACE, "Signing server_key_exchange message.\n"));
|
||
|
cbSignature = cbServerModulus;
|
||
|
if(!SchCryptSignHash(hHash,
|
||
|
pCred->dwKeySpec,
|
||
|
NULL,
|
||
|
0,
|
||
|
pbMessage + 2,
|
||
|
&cbSignature,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
DebugLog((DEB_TRACE, "Server_key_exchange message signed successfully.\n"));
|
||
|
SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
|
||
|
|
||
|
pbMessage[0] = MSBOF(cbSignature);
|
||
|
pbMessage[1] = LSBOF(cbSignature);
|
||
|
pbMessage += 2;
|
||
|
|
||
|
// Reverse signature.
|
||
|
for(i = 0; i < cbSignature / 2; i++)
|
||
|
{
|
||
|
BYTE n = pbMessage[i];
|
||
|
pbMessage[i] = pbMessage[cbSignature - i -1];
|
||
|
pbMessage[cbSignature - i -1] = n;
|
||
|
}
|
||
|
pbMessage += cbSignature;
|
||
|
|
||
|
*pcbServerExchangeValue = (DWORD)(pbMessage - pServerExchangeValue);
|
||
|
|
||
|
// Use ephemeral key for the new connection.
|
||
|
pContext->RipeZombie->hMasterProv = hEphemeralProv;
|
||
|
pContext->RipeZombie->dwFlags |= SP_CACHE_FLAG_MASTER_EPHEM;
|
||
|
|
||
|
pctRet = PCT_ERR_OK;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if(fImpersonating)
|
||
|
{
|
||
|
RevertToSelf();
|
||
|
}
|
||
|
|
||
|
return pctRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
SP_STATUS
|
||
|
WINAPI
|
||
|
PkcsGenerateClientExchangeValue(
|
||
|
SPContext * pContext, // in
|
||
|
PUCHAR pServerExchangeValue, // in
|
||
|
DWORD cbServerExchangeValue, // in
|
||
|
PUCHAR pClientClearValue, // out
|
||
|
DWORD * pcbClientClearValue, // in/out
|
||
|
PUCHAR pClientExchangeValue, // out
|
||
|
DWORD * pcbClientExchangeValue) // in/out
|
||
|
{
|
||
|
PSPCredentialGroup pCred;
|
||
|
DWORD cbSecret;
|
||
|
DWORD cbMasterKey;
|
||
|
HCRYPTKEY hServerPublic = 0;
|
||
|
DWORD dwGenFlags = 0;
|
||
|
DWORD dwExportFlags = 0;
|
||
|
SP_STATUS pctRet = PCT_ERR_OK;
|
||
|
BLOBHEADER *pPublicBlob;
|
||
|
DWORD cbPublicBlob;
|
||
|
DWORD cbHeader;
|
||
|
ALG_ID Algid = 0;
|
||
|
DWORD cbData;
|
||
|
DWORD cbEncryptedKey;
|
||
|
DWORD dwEnabledProtocols;
|
||
|
DWORD dwHighestProtocol;
|
||
|
|
||
|
if(pContext->RipeZombie->hMasterProv == 0)
|
||
|
{
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
pCred = pContext->pCredGroup;
|
||
|
if(pCred == NULL)
|
||
|
{
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
// We're doing a full handshake.
|
||
|
pContext->Flags |= CONTEXT_FLAG_FULL_HANDSHAKE;
|
||
|
|
||
|
//
|
||
|
// Determine highest supported protocol.
|
||
|
//
|
||
|
|
||
|
dwEnabledProtocols = pContext->dwClientEnabledProtocols;
|
||
|
|
||
|
if(dwEnabledProtocols & SP_PROT_TLS1_CLIENT)
|
||
|
{
|
||
|
dwHighestProtocol = TLS1_CLIENT_VERSION;
|
||
|
}
|
||
|
else if(dwEnabledProtocols & SP_PROT_SSL3_CLIENT)
|
||
|
{
|
||
|
dwHighestProtocol = SSL3_CLIENT_VERSION;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dwHighestProtocol = SSL2_CLIENT_VERSION;
|
||
|
}
|
||
|
|
||
|
// Get key length.
|
||
|
cbSecret = pContext->pPendingCipherInfo->cbSecret;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Import server's public key.
|
||
|
//
|
||
|
|
||
|
pPublicBlob = pContext->RipeZombie->pRemotePublic->pPublic;
|
||
|
cbPublicBlob = pContext->RipeZombie->pRemotePublic->cbPublic;
|
||
|
|
||
|
cbEncryptedKey = sizeof(BLOBHEADER) + sizeof(ALG_ID) + cbPublicBlob;
|
||
|
|
||
|
if(pClientExchangeValue == NULL)
|
||
|
{
|
||
|
*pcbClientExchangeValue = cbEncryptedKey;
|
||
|
pctRet = PCT_ERR_OK;
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
if(*pcbClientExchangeValue < cbEncryptedKey)
|
||
|
{
|
||
|
*pcbClientExchangeValue = cbEncryptedKey;
|
||
|
pctRet = SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
if(!SchCryptImportKey(pContext->RipeZombie->hMasterProv,
|
||
|
(PBYTE)pPublicBlob,
|
||
|
cbPublicBlob,
|
||
|
0,
|
||
|
0,
|
||
|
&hServerPublic,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Do protocol specific stuff.
|
||
|
//
|
||
|
|
||
|
switch(pContext->RipeZombie->fProtocol)
|
||
|
{
|
||
|
case SP_PROT_PCT1_CLIENT:
|
||
|
Algid = CALG_PCT1_MASTER;
|
||
|
dwGenFlags = CRYPT_EXPORTABLE;
|
||
|
|
||
|
// Generate the clear key value.
|
||
|
if(cbSecret < PCT1_MASTER_KEY_SIZE)
|
||
|
{
|
||
|
pContext->RipeZombie->cbClearKey = PCT1_MASTER_KEY_SIZE - cbSecret;
|
||
|
GenerateRandomBits( pContext->RipeZombie->pClearKey,
|
||
|
pContext->RipeZombie->cbClearKey);
|
||
|
|
||
|
*pcbClientClearValue = pContext->RipeZombie->cbClearKey;
|
||
|
CopyMemory( pClientClearValue,
|
||
|
pContext->RipeZombie->pClearKey,
|
||
|
pContext->RipeZombie->cbClearKey);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pcbClientClearValue = pContext->RipeZombie->cbClearKey = 0;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case SP_PROT_SSL2_CLIENT:
|
||
|
Algid = CALG_SSL2_MASTER;
|
||
|
dwGenFlags = CRYPT_EXPORTABLE;
|
||
|
|
||
|
cbMasterKey = pContext->pPendingCipherInfo->cbKey;
|
||
|
|
||
|
dwGenFlags |= ((cbSecret << 3) << 16);
|
||
|
|
||
|
// Generate the clear key value.
|
||
|
pContext->RipeZombie->cbClearKey = cbMasterKey - cbSecret;
|
||
|
|
||
|
if(pContext->RipeZombie->cbClearKey > 0)
|
||
|
{
|
||
|
GenerateRandomBits(pContext->RipeZombie->pClearKey,
|
||
|
pContext->RipeZombie->cbClearKey);
|
||
|
|
||
|
CopyMemory(pClientClearValue,
|
||
|
pContext->RipeZombie->pClearKey,
|
||
|
pContext->RipeZombie->cbClearKey);
|
||
|
}
|
||
|
*pcbClientClearValue = pContext->RipeZombie->cbClearKey;
|
||
|
|
||
|
if(dwEnabledProtocols & (SP_PROT_SSL3 | SP_PROT_TLS1))
|
||
|
{
|
||
|
// If we're a client doing SSL2, and
|
||
|
// SSL3 is enabled, then for some reason
|
||
|
// the server requested SSL2. Maybe
|
||
|
// A man in the middle changed the server
|
||
|
// version in the server hello to roll
|
||
|
// back. Pad with 8 0x03's so the server
|
||
|
// can detect this.
|
||
|
dwExportFlags = CRYPT_SSL2_FALLBACK;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case SP_PROT_TLS1_CLIENT:
|
||
|
Algid = CALG_TLS1_MASTER;
|
||
|
|
||
|
// drop through to SSL3
|
||
|
|
||
|
case SP_PROT_SSL3_CLIENT:
|
||
|
|
||
|
dwGenFlags = CRYPT_EXPORTABLE;
|
||
|
if(0 == Algid)
|
||
|
{
|
||
|
Algid = CALG_SSL3_MASTER;
|
||
|
}
|
||
|
|
||
|
// Generate the clear key value (always empty).
|
||
|
pContext->RipeZombie->cbClearKey = 0;
|
||
|
if(pcbClientClearValue) *pcbClientClearValue = 0;
|
||
|
|
||
|
if(cbServerExchangeValue && pServerExchangeValue)
|
||
|
{
|
||
|
// In ssl3, we look at the server exchange value.
|
||
|
// It may be a 512-bit public key, signed
|
||
|
// by the server public key. In this case, we need to
|
||
|
// use that as our master_secret encryption key.
|
||
|
HCRYPTKEY hNewServerPublic;
|
||
|
|
||
|
pctRet = Ssl3ParseServerKeyExchange(pContext,
|
||
|
pServerExchangeValue,
|
||
|
cbServerExchangeValue,
|
||
|
hServerPublic,
|
||
|
&hNewServerPublic);
|
||
|
if(pctRet != PCT_ERR_OK)
|
||
|
{
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
// Destroy public key from certificate.
|
||
|
SchCryptDestroyKey(hServerPublic, pContext->RipeZombie->dwCapiFlags);
|
||
|
|
||
|
// Use public key from ServerKeyExchange instead.
|
||
|
hServerPublic = hNewServerPublic;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
// Generate the master_secret.
|
||
|
if(!SchCryptGenKey(pContext->RipeZombie->hMasterProv,
|
||
|
Algid,
|
||
|
dwGenFlags,
|
||
|
&pContext->RipeZombie->hMasterKey,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
#if 1
|
||
|
// This is currently commented out because when connecting to a server running
|
||
|
// an old version of schannel (NT4 SP3 or so), then we will connect using SSL3,
|
||
|
// but the highest supported protocol is 0x0301. This confuses the server and
|
||
|
// it drops the connection.
|
||
|
|
||
|
// Set highest supported protocol. The CSP will place this version number
|
||
|
// in the pre_master_secret.
|
||
|
if(!SchCryptSetKeyParam(pContext->RipeZombie->hMasterKey,
|
||
|
KP_HIGHEST_VERSION,
|
||
|
(PBYTE)&dwHighestProtocol,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// Encrypt the master_secret.
|
||
|
DebugLog((DEB_TRACE, "Encrypt the master secret.\n"));
|
||
|
if(!SchCryptExportKey(pContext->RipeZombie->hMasterKey,
|
||
|
hServerPublic,
|
||
|
SIMPLEBLOB,
|
||
|
dwExportFlags,
|
||
|
pClientExchangeValue,
|
||
|
&cbEncryptedKey,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto done;
|
||
|
}
|
||
|
DebugLog((DEB_TRACE, "Master secret encrypted successfully.\n"));
|
||
|
|
||
|
// Determine size of key exchange key.
|
||
|
cbData = sizeof(DWORD);
|
||
|
if(!SchCryptGetKeyParam(hServerPublic,
|
||
|
KP_BLOCKLEN,
|
||
|
(PBYTE)&pContext->RipeZombie->dwExchStrength,
|
||
|
&cbData,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pContext->RipeZombie->dwExchStrength = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Strip off the blob header and copy the encrypted master_secret
|
||
|
// to the output buffer. Note that it is also converted to big endian.
|
||
|
cbHeader = sizeof(BLOBHEADER) + sizeof(ALG_ID);
|
||
|
cbEncryptedKey -= cbHeader;
|
||
|
if(pContext->RipeZombie->fProtocol == SP_PROT_TLS1_CLIENT)
|
||
|
{
|
||
|
MoveMemory(pClientExchangeValue + 2, pClientExchangeValue + cbHeader, cbEncryptedKey);
|
||
|
ReverseInPlace(pClientExchangeValue + 2, cbEncryptedKey);
|
||
|
|
||
|
pClientExchangeValue[0] = MSBOF(cbEncryptedKey);
|
||
|
pClientExchangeValue[1] = LSBOF(cbEncryptedKey);
|
||
|
|
||
|
*pcbClientExchangeValue = 2 + cbEncryptedKey;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
MoveMemory(pClientExchangeValue, pClientExchangeValue + cbHeader, cbEncryptedKey);
|
||
|
ReverseInPlace(pClientExchangeValue, cbEncryptedKey);
|
||
|
|
||
|
*pcbClientExchangeValue = cbEncryptedKey;
|
||
|
}
|
||
|
|
||
|
// Build the session keys.
|
||
|
pctRet = MakeSessionKeys(pContext,
|
||
|
pContext->RipeZombie->hMasterProv,
|
||
|
pContext->RipeZombie->hMasterKey);
|
||
|
if(pctRet != PCT_ERR_OK)
|
||
|
{
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
// Update perf counter.
|
||
|
InterlockedIncrement(&g_cClientHandshakes);
|
||
|
|
||
|
pctRet = PCT_ERR_OK;
|
||
|
|
||
|
done:
|
||
|
if(hServerPublic) SchCryptDestroyKey(hServerPublic, pContext->RipeZombie->dwCapiFlags);
|
||
|
|
||
|
return pctRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
SP_STATUS
|
||
|
GenerateRandomMasterKey(
|
||
|
PSPContext pContext,
|
||
|
HCRYPTKEY * phMasterKey)
|
||
|
{
|
||
|
DWORD dwGenFlags = 0;
|
||
|
ALG_ID Algid = 0;
|
||
|
DWORD cbSecret;
|
||
|
|
||
|
cbSecret = pContext->pPendingCipherInfo->cbSecret;
|
||
|
|
||
|
switch(pContext->RipeZombie->fProtocol)
|
||
|
{
|
||
|
case SP_PROT_PCT1_SERVER:
|
||
|
Algid = CALG_PCT1_MASTER;
|
||
|
dwGenFlags = CRYPT_EXPORTABLE;
|
||
|
break;
|
||
|
|
||
|
case SP_PROT_SSL2_SERVER:
|
||
|
Algid = CALG_SSL2_MASTER;
|
||
|
dwGenFlags = CRYPT_EXPORTABLE;
|
||
|
dwGenFlags |= ((cbSecret << 3) << 16);
|
||
|
break;
|
||
|
|
||
|
case SP_PROT_TLS1_SERVER:
|
||
|
Algid = CALG_TLS1_MASTER;
|
||
|
dwGenFlags = CRYPT_EXPORTABLE;
|
||
|
break;
|
||
|
|
||
|
case SP_PROT_SSL3_SERVER:
|
||
|
Algid = CALG_SSL3_MASTER;
|
||
|
dwGenFlags = CRYPT_EXPORTABLE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
// Generate the master_secret.
|
||
|
if(!SchCryptGenKey(pContext->RipeZombie->hMasterProv,
|
||
|
Algid,
|
||
|
dwGenFlags,
|
||
|
phMasterKey,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
return PCT_ERR_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: PkcsGenerateServerMasterKey
|
||
|
//
|
||
|
// Synopsis: Decrypt the master secret (from the ClientKeyExchange message)
|
||
|
// and derive the session keys from it.
|
||
|
//
|
||
|
// Arguments: [pContext] -- Schannel context.
|
||
|
// [pClientClearValue] -- Not used.
|
||
|
// [cbClientClearValue] -- Not used.
|
||
|
// [pClientExchangeValue] -- Pointer PKCS #2 block.
|
||
|
// [cbClientExchangeValue] -- Length of block.
|
||
|
//
|
||
|
// History: 10-02-97 jbanes Created.
|
||
|
//
|
||
|
// Notes: This routine is called by the server-side only.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
SP_STATUS
|
||
|
WINAPI
|
||
|
PkcsGenerateServerMasterKey(
|
||
|
PSPContext pContext, // in, out
|
||
|
PUCHAR pClientClearValue, // in
|
||
|
DWORD cbClientClearValue, // in
|
||
|
PUCHAR pClientExchangeValue, // in
|
||
|
DWORD cbClientExchangeValue) // in
|
||
|
{
|
||
|
PSPCredentialGroup pCred;
|
||
|
PBYTE pbBlob = NULL;
|
||
|
DWORD cbBlob;
|
||
|
ALG_ID Algid;
|
||
|
HCRYPTKEY hMasterKey;
|
||
|
HCRYPTKEY hExchKey = 0;
|
||
|
DWORD dwFlags = 0;
|
||
|
SP_STATUS pctRet;
|
||
|
DWORD cbData;
|
||
|
DWORD dwEnabledProtocols;
|
||
|
DWORD dwHighestProtocol;
|
||
|
BOOL fImpersonating = FALSE;
|
||
|
|
||
|
pCred = pContext->RipeZombie->pServerCred;
|
||
|
if(pCred == NULL)
|
||
|
{
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
dwEnabledProtocols = (g_ProtEnabled & pCred->grbitEnabledProtocols);
|
||
|
|
||
|
if(dwEnabledProtocols & SP_PROT_TLS1_SERVER)
|
||
|
{
|
||
|
dwHighestProtocol = TLS1_CLIENT_VERSION;
|
||
|
}
|
||
|
else if(dwEnabledProtocols & SP_PROT_SSL3_SERVER)
|
||
|
{
|
||
|
dwHighestProtocol = SSL3_CLIENT_VERSION;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dwHighestProtocol = SSL2_CLIENT_VERSION;
|
||
|
}
|
||
|
|
||
|
// We're doing a full handshake.
|
||
|
pContext->Flags |= CONTEXT_FLAG_FULL_HANDSHAKE;
|
||
|
|
||
|
// Determine encryption algid
|
||
|
switch(pContext->RipeZombie->fProtocol)
|
||
|
{
|
||
|
case SP_PROT_PCT1_SERVER:
|
||
|
Algid = CALG_PCT1_MASTER;
|
||
|
|
||
|
CopyMemory(pContext->RipeZombie->pClearKey,
|
||
|
pClientClearValue,
|
||
|
cbClientClearValue);
|
||
|
pContext->RipeZombie->cbClearKey = cbClientClearValue;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case SP_PROT_SSL2_SERVER:
|
||
|
Algid = CALG_SSL2_MASTER;
|
||
|
|
||
|
if(dwEnabledProtocols & (SP_PROT_SSL3 | SP_PROT_TLS1))
|
||
|
{
|
||
|
// We're a server doing SSL2, and we also support SSL3.
|
||
|
// If the encryption block contains the 8 0x03 padding
|
||
|
// bytes, then abort the connection.
|
||
|
dwFlags = CRYPT_SSL2_FALLBACK;
|
||
|
}
|
||
|
|
||
|
CopyMemory(pContext->RipeZombie->pClearKey,
|
||
|
pClientClearValue,
|
||
|
cbClientClearValue);
|
||
|
pContext->RipeZombie->cbClearKey = cbClientClearValue;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case SP_PROT_SSL3_SERVER:
|
||
|
Algid = CALG_SSL3_MASTER;
|
||
|
break;
|
||
|
|
||
|
case SP_PROT_TLS1_SERVER:
|
||
|
Algid = CALG_TLS1_MASTER;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
// Remove (pseudo-optional) vector in front of the encrypted master key.
|
||
|
if(pContext->RipeZombie->fProtocol == SP_PROT_SSL3_SERVER ||
|
||
|
pContext->RipeZombie->fProtocol == SP_PROT_TLS1_SERVER)
|
||
|
{
|
||
|
DWORD cbMsg = MAKEWORD(pClientExchangeValue[1], pClientExchangeValue[0]);
|
||
|
|
||
|
if(cbMsg + 2 == cbClientExchangeValue)
|
||
|
{
|
||
|
pClientExchangeValue += 2;
|
||
|
cbClientExchangeValue -= 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Allocate memory for blob.
|
||
|
cbBlob = sizeof(BLOBHEADER) + sizeof(ALG_ID) + cbClientExchangeValue;
|
||
|
pbBlob = SPExternalAlloc(cbBlob);
|
||
|
if(pbBlob == NULL)
|
||
|
{
|
||
|
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Build SIMPLEBLOB.
|
||
|
{
|
||
|
BLOBHEADER *pBlobHeader = (BLOBHEADER *)pbBlob;
|
||
|
ALG_ID *pAlgid = (ALG_ID *)(pBlobHeader + 1);
|
||
|
BYTE *pData = (BYTE *)(pAlgid + 1);
|
||
|
|
||
|
pBlobHeader->bType = SIMPLEBLOB;
|
||
|
pBlobHeader->bVersion = CUR_BLOB_VERSION;
|
||
|
pBlobHeader->reserved = 0;
|
||
|
pBlobHeader->aiKeyAlg = Algid;
|
||
|
|
||
|
*pAlgid = CALG_RSA_KEYX;
|
||
|
ReverseMemCopy(pData, pClientExchangeValue, cbClientExchangeValue);
|
||
|
}
|
||
|
|
||
|
DebugLog((DEB_TRACE, "Decrypt the master secret.\n"));
|
||
|
|
||
|
if(!(pContext->RipeZombie->dwFlags & SP_CACHE_FLAG_MASTER_EPHEM))
|
||
|
{
|
||
|
fImpersonating = SslImpersonateClient();
|
||
|
}
|
||
|
|
||
|
// Decrypt the master_secret.
|
||
|
if(!SchCryptGetUserKey(pContext->RipeZombie->hMasterProv,
|
||
|
AT_KEYEXCHANGE,
|
||
|
&hExchKey,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if(!SchCryptImportKey(pContext->RipeZombie->hMasterProv,
|
||
|
pbBlob,
|
||
|
cbBlob,
|
||
|
hExchKey,
|
||
|
dwFlags,
|
||
|
&hMasterKey,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
DebugLog((DEB_TRACE, "Master secret did not decrypt correctly.\n"));
|
||
|
|
||
|
// Guard against the PKCS#1 attack by generating a
|
||
|
// random master key.
|
||
|
pctRet = GenerateRandomMasterKey(pContext, &hMasterKey);
|
||
|
if(pctRet != PCT_ERR_OK)
|
||
|
{
|
||
|
pctRet = PCT_INT_INTERNAL_ERROR;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugLog((DEB_TRACE, "Master secret decrypted successfully.\n"));
|
||
|
|
||
|
// Set highest supported protocol. The CSP will use this to check for
|
||
|
// version fallback attacks.
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_HIGHEST_VERSION,
|
||
|
(PBYTE)&dwHighestProtocol,
|
||
|
CRYPT_SERVER,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
|
||
|
if(GetLastError() == NTE_BAD_VER)
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
SchCryptDestroyKey(hMasterKey, pContext->RipeZombie->dwCapiFlags);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pContext->RipeZombie->hMasterKey = hMasterKey;
|
||
|
|
||
|
// Determine size of key exchange key.
|
||
|
cbData = sizeof(DWORD);
|
||
|
if(!SchCryptGetKeyParam(hExchKey,
|
||
|
KP_BLOCKLEN,
|
||
|
(PBYTE)&pContext->RipeZombie->dwExchStrength,
|
||
|
&cbData,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
pContext->RipeZombie->dwExchStrength = 0;
|
||
|
}
|
||
|
|
||
|
SchCryptDestroyKey(hExchKey, pContext->RipeZombie->dwCapiFlags);
|
||
|
hExchKey = 0;
|
||
|
|
||
|
// Build the session keys.
|
||
|
pctRet = MakeSessionKeys(pContext,
|
||
|
pContext->RipeZombie->hMasterProv,
|
||
|
hMasterKey);
|
||
|
if(pctRet != PCT_ERR_OK)
|
||
|
{
|
||
|
SP_LOG_RESULT(pctRet);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
// Update perf counter.
|
||
|
InterlockedIncrement(&g_cServerHandshakes);
|
||
|
|
||
|
pctRet = PCT_ERR_OK;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if(fImpersonating)
|
||
|
{
|
||
|
RevertToSelf();
|
||
|
}
|
||
|
|
||
|
if(pbBlob != NULL)
|
||
|
{
|
||
|
SPExternalFree(pbBlob);
|
||
|
}
|
||
|
|
||
|
if(hExchKey)
|
||
|
{
|
||
|
SchCryptDestroyKey(hExchKey, pContext->RipeZombie->dwCapiFlags);
|
||
|
}
|
||
|
|
||
|
return pctRet;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: PkcsFinishMasterKey
|
||
|
//
|
||
|
// Synopsis: Complete the derivation of the master key by programming the
|
||
|
// CSP with the (protocol dependent) auxilary plaintext
|
||
|
// information.
|
||
|
//
|
||
|
// Arguments: [pContext] -- Schannel context.
|
||
|
// [hMasterKey] -- Handle to master key.
|
||
|
//
|
||
|
// History: 10-03-97 jbanes Created.
|
||
|
//
|
||
|
// Notes: This routine is called by the server-side only.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
SP_STATUS
|
||
|
PkcsFinishMasterKey(
|
||
|
PSPContext pContext, // in, out
|
||
|
HCRYPTKEY hMasterKey) // in
|
||
|
{
|
||
|
PCipherInfo pCipherInfo = NULL;
|
||
|
PHashInfo pHashInfo = NULL;
|
||
|
SCHANNEL_ALG Algorithm;
|
||
|
BOOL fExportable = TRUE;
|
||
|
DWORD dwCipherFlags;
|
||
|
|
||
|
if(pContext->RipeZombie->hMasterProv == 0)
|
||
|
{
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
// Get pointer to pending cipher system.
|
||
|
pCipherInfo = pContext->pPendingCipherInfo;
|
||
|
|
||
|
// Get pointer to pending hash system.
|
||
|
pHashInfo = pContext->pPendingHashInfo;
|
||
|
|
||
|
// Determine whether this is an "exportable" cipher.
|
||
|
if(pContext->dwPendingCipherSuiteIndex)
|
||
|
{
|
||
|
// Use cipher suite flags (SSL3 & TLS).
|
||
|
dwCipherFlags = UniAvailableCiphers[pContext->dwPendingCipherSuiteIndex].dwFlags;
|
||
|
|
||
|
if(dwCipherFlags & DOMESTIC_CIPHER_SUITE)
|
||
|
{
|
||
|
fExportable = FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Use key length (PCT & SSL2).
|
||
|
if(pCipherInfo->dwStrength > 40)
|
||
|
{
|
||
|
fExportable = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Specify encryption algorithm.
|
||
|
if(pCipherInfo->aiCipher != CALG_NULLCIPHER)
|
||
|
{
|
||
|
ZeroMemory(&Algorithm, sizeof(Algorithm));
|
||
|
Algorithm.dwUse = SCHANNEL_ENC_KEY;
|
||
|
Algorithm.Algid = pCipherInfo->aiCipher;
|
||
|
Algorithm.cBits = pCipherInfo->cbSecret * 8;
|
||
|
if(fExportable)
|
||
|
{
|
||
|
Algorithm.dwFlags = INTERNATIONAL_USAGE;
|
||
|
}
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_SCHANNEL_ALG,
|
||
|
(PBYTE)&Algorithm,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Specify hash algorithm.
|
||
|
Algorithm.dwUse = SCHANNEL_MAC_KEY;
|
||
|
Algorithm.Algid = pHashInfo->aiHash;
|
||
|
Algorithm.cBits = pHashInfo->cbCheckSum * 8;
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_SCHANNEL_ALG,
|
||
|
(PBYTE)&Algorithm,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
// Finish creating the master_secret.
|
||
|
switch(pContext->RipeZombie->fProtocol)
|
||
|
{
|
||
|
case SP_PROT_PCT1_CLIENT:
|
||
|
case SP_PROT_PCT1_SERVER:
|
||
|
{
|
||
|
CRYPT_DATA_BLOB Data;
|
||
|
|
||
|
// Specify clear key value.
|
||
|
if(pContext->RipeZombie->cbClearKey)
|
||
|
{
|
||
|
Data.pbData = pContext->RipeZombie->pClearKey;
|
||
|
Data.cbData = pContext->RipeZombie->cbClearKey;
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_CLEAR_KEY,
|
||
|
(BYTE*)&Data,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Specify the CH_CHALLENGE_DATA.
|
||
|
Data.pbData = pContext->pChallenge;
|
||
|
Data.cbData = pContext->cbChallenge;
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_CLIENT_RANDOM,
|
||
|
(BYTE*)&Data,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
// Specify the SH_CONNECTION_ID_DATA.
|
||
|
Data.pbData = pContext->pConnectionID;
|
||
|
Data.cbData = pContext->cbConnectionID;
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_SERVER_RANDOM,
|
||
|
(BYTE*)&Data,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
// Specify the SH_CERTIFICATE_DATA.
|
||
|
Data.pbData = pContext->RipeZombie->pbServerCertificate;
|
||
|
Data.cbData = pContext->RipeZombie->cbServerCertificate;
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_CERTIFICATE,
|
||
|
(BYTE*)&Data,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case SP_PROT_SSL2_CLIENT:
|
||
|
case SP_PROT_SSL2_SERVER:
|
||
|
{
|
||
|
CRYPT_DATA_BLOB Data;
|
||
|
|
||
|
// Specify clear key value.
|
||
|
if(pContext->RipeZombie->cbClearKey)
|
||
|
{
|
||
|
Data.pbData = pContext->RipeZombie->pClearKey;
|
||
|
Data.cbData = pContext->RipeZombie->cbClearKey;
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_CLEAR_KEY,
|
||
|
(BYTE*)&Data,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Specify the CH_CHALLENGE_DATA.
|
||
|
Data.pbData = pContext->pChallenge;
|
||
|
Data.cbData = pContext->cbChallenge;
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_CLIENT_RANDOM,
|
||
|
(BYTE*)&Data,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
// Specify the SH_CONNECTION_ID_DATA.
|
||
|
Data.pbData = pContext->pConnectionID;
|
||
|
Data.cbData = pContext->cbConnectionID;
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_SERVER_RANDOM,
|
||
|
(BYTE*)&Data,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case SP_PROT_SSL3_CLIENT:
|
||
|
case SP_PROT_SSL3_SERVER:
|
||
|
case SP_PROT_TLS1_CLIENT:
|
||
|
case SP_PROT_TLS1_SERVER:
|
||
|
{
|
||
|
CRYPT_DATA_BLOB Data;
|
||
|
|
||
|
// Specify client_random.
|
||
|
Data.pbData = pContext->rgbS3CRandom;
|
||
|
Data.cbData = CB_SSL3_RANDOM;
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_CLIENT_RANDOM,
|
||
|
(BYTE*)&Data,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
// Specify server_random.
|
||
|
Data.pbData = pContext->rgbS3SRandom;
|
||
|
Data.cbData = CB_SSL3_RANDOM;
|
||
|
if(!SchCryptSetKeyParam(hMasterKey,
|
||
|
KP_SERVER_RANDOM,
|
||
|
(BYTE*)&Data,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
return PCT_INT_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
return PCT_ERR_OK;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: MakeSessionKeys
|
||
|
//
|
||
|
// Synopsis: Derive the session keys from the completed master key.
|
||
|
//
|
||
|
// Arguments: [pContext] -- Schannel context.
|
||
|
// [hProv] --
|
||
|
// [hMasterKey] -- Handle to master key.
|
||
|
//
|
||
|
// History: 10-03-97 jbanes Created.
|
||
|
//
|
||
|
// Notes: This routine is called by the server-side only.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
SP_STATUS
|
||
|
MakeSessionKeys(
|
||
|
PSPContext pContext, // in
|
||
|
HCRYPTPROV hProv, // in
|
||
|
HCRYPTKEY hMasterKey) // in
|
||
|
{
|
||
|
HCRYPTHASH hMasterHash = 0;
|
||
|
HCRYPTKEY hLocalMasterKey = 0;
|
||
|
BOOL fClient;
|
||
|
SP_STATUS pctRet;
|
||
|
|
||
|
//
|
||
|
// Duplicate the master key if we're doing a reconnect handshake. This
|
||
|
// will allow us to set the client_random and server_random properties
|
||
|
// on the key without having to worry about different threads
|
||
|
// interferring with each other.
|
||
|
//
|
||
|
|
||
|
if((pContext->Flags & CONTEXT_FLAG_FULL_HANDSHAKE) == 0)
|
||
|
{
|
||
|
if(!CryptDuplicateKey(hMasterKey, NULL, 0, &hLocalMasterKey))
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(GetLastError());
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
hMasterKey = hLocalMasterKey;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Finish the master_secret.
|
||
|
pctRet = PkcsFinishMasterKey(pContext, hMasterKey);
|
||
|
if(pctRet != PCT_ERR_OK)
|
||
|
{
|
||
|
SP_LOG_RESULT(pctRet);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
fClient = !(pContext->RipeZombie->fProtocol & SP_PROT_SERVERS);
|
||
|
|
||
|
// Create the master hash object from the master_secret key.
|
||
|
if(!SchCryptCreateHash(hProv,
|
||
|
CALG_SCHANNEL_MASTER_HASH,
|
||
|
hMasterKey,
|
||
|
0,
|
||
|
&hMasterHash,
|
||
|
0))
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(GetLastError());
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Derive read key from the master hash object.
|
||
|
if(pContext->hPendingReadKey)
|
||
|
{
|
||
|
SchCryptDestroyKey(pContext->hPendingReadKey, 0);
|
||
|
pContext->hPendingReadKey = 0;
|
||
|
}
|
||
|
if(pContext->pPendingCipherInfo->aiCipher != CALG_NULLCIPHER)
|
||
|
{
|
||
|
if(!SchCryptDeriveKey(hProv,
|
||
|
CALG_SCHANNEL_ENC_KEY,
|
||
|
hMasterHash,
|
||
|
CRYPT_EXPORTABLE | (fClient ? CRYPT_SERVER : 0),
|
||
|
&pContext->hPendingReadKey,
|
||
|
0))
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(GetLastError());
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Derive write key from the master hash object.
|
||
|
if(pContext->hPendingWriteKey)
|
||
|
{
|
||
|
SchCryptDestroyKey(pContext->hPendingWriteKey, 0);
|
||
|
pContext->hPendingWriteKey = 0;
|
||
|
}
|
||
|
if(pContext->pPendingCipherInfo->aiCipher != CALG_NULLCIPHER)
|
||
|
{
|
||
|
if(!SchCryptDeriveKey(hProv,
|
||
|
CALG_SCHANNEL_ENC_KEY,
|
||
|
hMasterHash,
|
||
|
CRYPT_EXPORTABLE | (fClient ? 0 : CRYPT_SERVER),
|
||
|
&pContext->hPendingWriteKey,
|
||
|
0))
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(GetLastError());
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if((pContext->RipeZombie->fProtocol & SP_PROT_SSL2) ||
|
||
|
(pContext->RipeZombie->fProtocol & SP_PROT_PCT1))
|
||
|
{
|
||
|
// Set the IV on the client and server encryption keys
|
||
|
if(!SchCryptSetKeyParam(pContext->hPendingReadKey,
|
||
|
KP_IV,
|
||
|
pContext->RipeZombie->pKeyArgs,
|
||
|
0,
|
||
|
0))
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(GetLastError());
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if(!SchCryptSetKeyParam(pContext->hPendingWriteKey,
|
||
|
KP_IV,
|
||
|
pContext->RipeZombie->pKeyArgs,
|
||
|
0,
|
||
|
0))
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(GetLastError());
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(pContext->RipeZombie->fProtocol & SP_PROT_SSL2)
|
||
|
{
|
||
|
// SSL 2.0 uses same set of keys for both encryption and MAC.
|
||
|
pContext->hPendingReadMAC = 0;
|
||
|
pContext->hPendingWriteMAC = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Derive read MAC from the master hash object.
|
||
|
if(pContext->hPendingReadMAC)
|
||
|
{
|
||
|
SchCryptDestroyKey(pContext->hPendingReadMAC, 0);
|
||
|
}
|
||
|
if(!SchCryptDeriveKey(hProv,
|
||
|
CALG_SCHANNEL_MAC_KEY,
|
||
|
hMasterHash,
|
||
|
CRYPT_EXPORTABLE | (fClient ? CRYPT_SERVER : 0),
|
||
|
&pContext->hPendingReadMAC,
|
||
|
0))
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(GetLastError());
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
// Derive write MAC from the master hash object.
|
||
|
if(pContext->hPendingWriteMAC)
|
||
|
{
|
||
|
SchCryptDestroyKey(pContext->hPendingWriteMAC, 0);
|
||
|
}
|
||
|
if(!SchCryptDeriveKey(hProv,
|
||
|
CALG_SCHANNEL_MAC_KEY,
|
||
|
hMasterHash,
|
||
|
CRYPT_EXPORTABLE | (fClient ? 0 : CRYPT_SERVER),
|
||
|
&pContext->hPendingWriteMAC,
|
||
|
0))
|
||
|
{
|
||
|
pctRet = SP_LOG_RESULT(GetLastError());
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pctRet = PCT_ERR_OK;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if(hMasterHash)
|
||
|
{
|
||
|
SchCryptDestroyHash(hMasterHash, 0);
|
||
|
}
|
||
|
|
||
|
if(hLocalMasterKey)
|
||
|
{
|
||
|
CryptDestroyKey(hLocalMasterKey);
|
||
|
}
|
||
|
|
||
|
return pctRet;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: Ssl3ParseServerKeyExchange
|
||
|
//
|
||
|
// Synopsis: Parse the ServerKeyExchange message and import modulus and
|
||
|
// exponent into a CryptoAPI public key.
|
||
|
//
|
||
|
// Arguments: [pContext] -- Schannel context.
|
||
|
//
|
||
|
// [pbMessage] -- Pointer to message.
|
||
|
//
|
||
|
// [cbMessage] -- Message length.
|
||
|
//
|
||
|
// [hServerPublic] -- Handle to public key from server's
|
||
|
// certificate. This is used to verify
|
||
|
// the message's signature.
|
||
|
//
|
||
|
// [phNewServerPublic] -- (output) Handle to new public key.
|
||
|
//
|
||
|
//
|
||
|
// History: 10-23-97 jbanes Created.
|
||
|
//
|
||
|
// Notes: This routine is called by the client-side only.
|
||
|
//
|
||
|
// The format of the ServerKeyExchange message is:
|
||
|
//
|
||
|
// struct {
|
||
|
// select (KeyExchangeAlgorithm) {
|
||
|
// case diffie_hellman:
|
||
|
// ServerDHParams params;
|
||
|
// Signature signed_params;
|
||
|
// case rsa:
|
||
|
// ServerRSAParams params;
|
||
|
// Signature signed_params;
|
||
|
// case fortezza_dms:
|
||
|
// ServerFortezzaParams params;
|
||
|
// };
|
||
|
// } ServerKeyExchange;
|
||
|
//
|
||
|
// struct {
|
||
|
// opaque rsa_modulus<1..2^16-1>;
|
||
|
// opaque rsa_exponent<1..2^16-1>;
|
||
|
// } ServerRSAParams;
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
SP_STATUS
|
||
|
Ssl3ParseServerKeyExchange(
|
||
|
PSPContext pContext, // in
|
||
|
PBYTE pbMessage, // in
|
||
|
DWORD cbMessage, // in
|
||
|
HCRYPTKEY hServerPublic, // in
|
||
|
HCRYPTKEY *phNewServerPublic) // out
|
||
|
{
|
||
|
PBYTE pbModulus = NULL;
|
||
|
DWORD cbModulus;
|
||
|
PBYTE pbExponent = NULL;
|
||
|
DWORD cbExponent;
|
||
|
PBYTE pbServerParams = NULL;
|
||
|
DWORD cbServerParams;
|
||
|
DWORD dwExponent;
|
||
|
SP_STATUS pctRet;
|
||
|
DWORD i;
|
||
|
|
||
|
if(pbMessage == NULL || cbMessage == 0)
|
||
|
{
|
||
|
*phNewServerPublic = 0;
|
||
|
return PCT_ERR_OK;
|
||
|
}
|
||
|
|
||
|
// Mark start of ServerRSAParams structure.
|
||
|
// This is used to build hash values.
|
||
|
pbServerParams = pbMessage;
|
||
|
|
||
|
// Modulus length
|
||
|
cbModulus = MAKEWORD(pbMessage[1], pbMessage[0]);
|
||
|
pbMessage += 2;
|
||
|
|
||
|
// Since the modulus is encoded as an INTEGER, it is padded with a leading
|
||
|
// zero if its most significant bit is one. Remove this padding, if
|
||
|
// present.
|
||
|
if(pbMessage[0] == 0)
|
||
|
{
|
||
|
cbModulus -= 1;
|
||
|
pbMessage += 1;
|
||
|
}
|
||
|
|
||
|
if(cbModulus < 512/8 || cbModulus > 1024/8)
|
||
|
{
|
||
|
return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
|
||
|
}
|
||
|
|
||
|
// Modulus
|
||
|
pbModulus = pbMessage;
|
||
|
pbMessage += cbModulus;
|
||
|
|
||
|
// Exponent length
|
||
|
cbExponent = MAKEWORD(pbMessage[1], pbMessage[0]);
|
||
|
if(cbExponent < 1 || cbExponent > 4)
|
||
|
{
|
||
|
return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
|
||
|
}
|
||
|
pbMessage += 2;
|
||
|
|
||
|
// Exponent
|
||
|
pbExponent = pbMessage;
|
||
|
pbMessage += cbExponent;
|
||
|
|
||
|
// form a (little endian) DWORD from exponent data
|
||
|
dwExponent = 0;
|
||
|
for(i = 0; i < cbExponent; i++)
|
||
|
{
|
||
|
dwExponent <<= 8;
|
||
|
dwExponent |= pbExponent[i];
|
||
|
}
|
||
|
|
||
|
// Compute length of ServerRSAParams structure.
|
||
|
cbServerParams = (DWORD)(pbMessage - pbServerParams);
|
||
|
|
||
|
//
|
||
|
// digitally-signed struct {
|
||
|
// select(SignatureAlgorithm) {
|
||
|
// case anonymous: struct { };
|
||
|
// case rsa:
|
||
|
// opaque md5_hash[16];
|
||
|
// opaque sha_hash[20];
|
||
|
// case dsa:
|
||
|
// opaque sha_hash[20];
|
||
|
// };
|
||
|
// } Signature;
|
||
|
//
|
||
|
|
||
|
{
|
||
|
BYTE rgbHashValue[CB_MD5_DIGEST_LEN + CB_SHA_DIGEST_LEN];
|
||
|
PBYTE pbSignature;
|
||
|
DWORD cbSignature;
|
||
|
HCRYPTHASH hHash;
|
||
|
PBYTE pbLocalBuffer;
|
||
|
DWORD cbLocalBuffer;
|
||
|
|
||
|
// Signature block length
|
||
|
cbSignature = ((INT)pbMessage[0] << 8) + pbMessage[1];
|
||
|
pbMessage += 2;
|
||
|
pbSignature = pbMessage;
|
||
|
|
||
|
// Allocate buffer for RSA operation.
|
||
|
cbLocalBuffer = cbSignature;
|
||
|
pbLocalBuffer = SPExternalAlloc(cbLocalBuffer);
|
||
|
if(pbLocalBuffer == NULL)
|
||
|
{
|
||
|
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
||
|
}
|
||
|
|
||
|
// Reverse the signature.
|
||
|
ReverseMemCopy(pbLocalBuffer, pbSignature, cbSignature);
|
||
|
|
||
|
// Compute MD5 and SHA hash values.
|
||
|
ComputeServerExchangeHashes(pContext,
|
||
|
pbServerParams,
|
||
|
cbServerParams,
|
||
|
rgbHashValue,
|
||
|
rgbHashValue + CB_MD5_DIGEST_LEN);
|
||
|
|
||
|
|
||
|
if(!SchCryptCreateHash(pContext->RipeZombie->hMasterProv,
|
||
|
CALG_SSL3_SHAMD5,
|
||
|
0,
|
||
|
0,
|
||
|
&hHash,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
SPExternalFree(pbLocalBuffer);
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
// set hash value
|
||
|
if(!SchCryptSetHashParam(hHash,
|
||
|
HP_HASHVAL,
|
||
|
rgbHashValue,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
|
||
|
SPExternalFree(pbLocalBuffer);
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
DebugLog((DEB_TRACE, "Verify server_key_exchange message signature.\n"));
|
||
|
if(!SchCryptVerifySignature(hHash,
|
||
|
pbLocalBuffer,
|
||
|
cbSignature,
|
||
|
hServerPublic,
|
||
|
NULL,
|
||
|
0,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
DebugLog((DEB_WARN, "Signature Verify Failed: %x\n", GetLastError()));
|
||
|
SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
|
||
|
SPExternalFree(pbLocalBuffer);
|
||
|
return SP_LOG_RESULT(PCT_ERR_INTEGRITY_CHECK_FAILED);
|
||
|
}
|
||
|
DebugLog((DEB_TRACE, "Server_key_exchange message signature verified okay.\n"));
|
||
|
|
||
|
SchCryptDestroyHash(hHash, pContext->RipeZombie->dwCapiFlags);
|
||
|
SPExternalFree(pbLocalBuffer);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Import ephemeral public key into CSP.
|
||
|
//
|
||
|
|
||
|
{
|
||
|
BLOBHEADER *pBlobHeader;
|
||
|
RSAPUBKEY *pRsaPubKey;
|
||
|
PBYTE pbBlob;
|
||
|
DWORD cbBlob;
|
||
|
|
||
|
// Allocate memory for PUBLICKEYBLOB.
|
||
|
cbBlob = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + cbModulus;
|
||
|
pbBlob = SPExternalAlloc(cbBlob);
|
||
|
if(pbBlob == NULL)
|
||
|
{
|
||
|
return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
||
|
}
|
||
|
|
||
|
// Build PUBLICKEYBLOB from modulus and exponent.
|
||
|
pBlobHeader = (BLOBHEADER *)pbBlob;
|
||
|
pRsaPubKey = (RSAPUBKEY *)(pBlobHeader + 1);
|
||
|
|
||
|
pBlobHeader->bType = PUBLICKEYBLOB;
|
||
|
pBlobHeader->bVersion = CUR_BLOB_VERSION;
|
||
|
pBlobHeader->reserved = 0;
|
||
|
pBlobHeader->aiKeyAlg = CALG_RSA_KEYX;
|
||
|
pRsaPubKey->magic = 0x31415352; // RSA1
|
||
|
pRsaPubKey->bitlen = cbModulus * 8;
|
||
|
pRsaPubKey->pubexp = dwExponent;
|
||
|
ReverseMemCopy((PBYTE)(pRsaPubKey + 1), pbModulus, cbModulus);
|
||
|
|
||
|
if(!SchCryptImportKey(pContext->RipeZombie->hMasterProv,
|
||
|
pbBlob,
|
||
|
cbBlob,
|
||
|
0,
|
||
|
0,
|
||
|
phNewServerPublic,
|
||
|
pContext->RipeZombie->dwCapiFlags))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
SPExternalFree(pbBlob);
|
||
|
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
|
||
|
}
|
||
|
|
||
|
SPExternalFree(pbBlob);
|
||
|
}
|
||
|
|
||
|
return PCT_ERR_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|