windows-nt/Source/XPSP1/NT/ds/security/protocols/schannel/spbase/keyxmsdh.c
2020-09-26 16:20:57 +08:00

1228 lines
31 KiB
C

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: keyxmsdh.c
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 10-21-97 jbanes CAPI integration stuff.
//
//----------------------------------------------------------------------------
#include <spbase.h>
#include <align.h>
// PROV_DH_SCHANNEL handle used for client and server operations. This is
// where the schannel ephemeral DH key lives.
HCRYPTPROV g_hDhSchannelProv = 0;
PROV_ENUMALGS_EX * g_pDhSchannelAlgs = NULL;
DWORD g_cDhSchannelAlgs = 0;
SP_STATUS
WINAPI
DHGenerateServerExchangeValue(
SPContext * pContext, // in
PUCHAR pServerExchangeValue, // out
DWORD * pcbServerExchangeValue // in/out
);
SP_STATUS
WINAPI
DHGenerateClientExchangeValue(
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
DHGenerateServerMasterKey(
SPContext * pContext, // in
PUCHAR pClientClearValue, // in
DWORD cbClientClearValue, // in
PUCHAR pClientExchangeValue, // in
DWORD cbClientExchangeValue // in
);
KeyExchangeSystem keyexchDH = {
SP_EXCH_DH_PKCS3,
"Diffie Hellman",
DHGenerateServerExchangeValue,
DHGenerateClientExchangeValue,
DHGenerateServerMasterKey,
};
SP_STATUS
SPSignDssParams(
PSPContext pContext,
PSPCredential pCred,
PBYTE pbParams,
DWORD cbParams,
PBYTE pbEncodedSignature,
PDWORD pcbEncodedSignature)
{
HCRYPTHASH hHash;
BYTE rgbSignature[DSA_SIGNATURE_SIZE];
DWORD cbSignature;
if(pCred == NULL)
{
return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
}
if(!SchCryptCreateHash(pCred->hProv,
CALG_SHA,
0,
0,
&hHash,
pCred->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
return PCT_ERR_ILLEGAL_MESSAGE;
}
if(!SchCryptHashData(hHash, pContext->rgbS3CRandom, 32, 0, pCred->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
CryptDestroyHash(hHash);
return PCT_ERR_ILLEGAL_MESSAGE;
}
if(!SchCryptHashData(hHash, pContext->rgbS3SRandom, 32, 0, pCred->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
CryptDestroyHash(hHash);
return PCT_ERR_ILLEGAL_MESSAGE;
}
if(!SchCryptHashData(hHash, pbParams, cbParams, 0, pCred->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
CryptDestroyHash(hHash);
return PCT_ERR_ILLEGAL_MESSAGE;
}
cbSignature = sizeof(rgbSignature);
if(!SchCryptSignHash(hHash,
pCred->dwKeySpec,
NULL,
0,
rgbSignature,
&cbSignature,
pCred->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
CryptDestroyHash(hHash);
return PCT_ERR_ILLEGAL_MESSAGE;
}
CryptDestroyHash(hHash);
if(!CryptEncodeObject(X509_ASN_ENCODING,
X509_DSS_SIGNATURE,
rgbSignature,
pbEncodedSignature,
pcbEncodedSignature))
{
SP_LOG_RESULT(GetLastError());
return PCT_ERR_ILLEGAL_MESSAGE;
}
// Return success.
return PCT_ERR_OK;
}
SP_STATUS
SPVerifyDssParams(
PSPContext pContext,
HCRYPTPROV hProv,
HCRYPTKEY hPublicKey,
DWORD dwCapiFlags,
PBYTE pbParams,
DWORD cbParams,
PBYTE pbEncodedSignature,
DWORD cbEncodedSignature)
{
HCRYPTHASH hHash;
BYTE rgbSignature[DSA_SIGNATURE_SIZE];
DWORD cbSignature;
// Decode the signature.
cbSignature = sizeof(rgbSignature);
if(!CryptDecodeObject(X509_ASN_ENCODING,
X509_DSS_SIGNATURE,
pbEncodedSignature,
cbEncodedSignature,
0,
rgbSignature,
&cbSignature))
{
SP_LOG_RESULT(GetLastError());
return PCT_ERR_ILLEGAL_MESSAGE;
}
if(!SchCryptCreateHash(hProv,
CALG_SHA,
0,
0,
&hHash,
dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
return PCT_ERR_ILLEGAL_MESSAGE;
}
if(!SchCryptHashData(hHash, pContext->rgbS3CRandom, 32, 0, dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
CryptDestroyHash(hHash);
return PCT_ERR_ILLEGAL_MESSAGE;
}
if(!SchCryptHashData(hHash, pContext->rgbS3SRandom, 32, 0, dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
CryptDestroyHash(hHash);
return PCT_ERR_ILLEGAL_MESSAGE;
}
if(!SchCryptHashData(hHash, pbParams, cbParams, 0, dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
CryptDestroyHash(hHash);
return PCT_ERR_ILLEGAL_MESSAGE;
}
if(!SchCryptVerifySignature(hHash,
rgbSignature,
cbSignature,
hPublicKey,
NULL,
0,
dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
CryptDestroyHash(hHash);
return PCT_INT_MSG_ALTERED;
}
CryptDestroyHash(hHash);
return PCT_ERR_OK;
}
SP_STATUS
GetDHEphemKey(
PSPContext pContext,
HCRYPTPROV * phProv,
HCRYPTKEY * phTek)
{
PSPCredentialGroup pCredGroup;
PSPCredential pCred;
DWORD dwKeySize;
DWORD cbData;
DWORD Status;
pCredGroup = pContext->RipeZombie->pServerCred;
if(pCredGroup == NULL)
{
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
}
pCred = pContext->RipeZombie->pActiveServerCred;
if(pCredGroup == NULL)
{
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
}
LockCredential(pCredGroup);
if(phProv)
{
*phProv = pCred->hProv;
}
dwKeySize = 1024;
// Determine if we've already created an ephemeral key.
if(pCred->hTek)
{
*phTek = pCred->hTek;
Status = PCT_ERR_OK;
goto cleanup;
}
// Generate the ephemeral key.
if(!CryptGenKey(pCred->hProv,
CALG_DH_EPHEM,
dwKeySize << 16,
phTek))
{
SP_LOG_RESULT(GetLastError());
Status = PCT_INT_INTERNAL_ERROR;
goto cleanup;
}
pCred->hTek = *phTek;
Status = PCT_ERR_OK;
cleanup:
if(Status == PCT_ERR_OK)
{
// Determine size of key exchange key.
cbData = sizeof(DWORD);
if(!SchCryptGetKeyParam(*phTek,
KP_BLOCKLEN,
(PBYTE)&pContext->RipeZombie->dwExchStrength,
&cbData,
0,
pContext->RipeZombie->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
pContext->RipeZombie->dwExchStrength = 0;
}
}
UnlockCredential(pCredGroup);
return Status;
}
//+---------------------------------------------------------------------------
//
// Function: DHGenerateServerExchangeValue
//
// Synopsis: Create a ServerKeyExchange message, containing an ephemeral
// DH key.
//
// Arguments: [pContext] -- Schannel context.
// [pServerExchangeValue] --
// [pcbServerExchangeValue] --
//
// History: 03-24-98 jbanes Added CAPI integration.
//
// Notes: The following data is placed in the output buffer by
// this routine:
//
// struct {
// opaque dh_p<1..2^16-1>;
// opaque dh_g<1..2^16-1>;
// opaque dh_Ys<1..2^16-1>;
// } ServerDHParams;
//
// struct {
// ServerDHParams params;
// Signature signed_params;
// } ServerKeyExchange;
//
//----------------------------------------------------------------------------
SP_STATUS
WINAPI
DHGenerateServerExchangeValue(
PSPContext pContext, // in
PBYTE pServerExchangeValue, // out
DWORD * pcbServerExchangeValue) // in/out
{
PSPCredential pCred;
HCRYPTPROV hProv = 0;
HCRYPTKEY hServerDhKey = 0;
PBYTE pbMessage;
DWORD cbMessage;
DWORD cbBytesLeft;
DWORD cbData;
DWORD cbP;
DWORD cbG;
DWORD cbY;
DWORD cbSignature;
SP_STATUS pctRet;
BOOL fImpersonating = FALSE;
pCred = pContext->RipeZombie->pActiveServerCred;
if(pCred == NULL)
{
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
}
if(pContext->RipeZombie->fProtocol != SP_PROT_SSL3_SERVER &&
pContext->RipeZombie->fProtocol != SP_PROT_TLS1_SERVER)
{
// SSL2 and PCT do not support DH.
return SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH);
}
// Always send a ServerKeyExchange message.
pContext->fExchKey = TRUE;
fImpersonating = SslImpersonateClient();
//
// Generate ephemeral DH key.
//
pctRet = GetDHEphemKey(pContext,
&hProv,
&hServerDhKey);
if(pctRet != PCT_ERR_OK)
{
SP_LOG_RESULT(pctRet);
goto cleanup;
}
//
// Estimate sizes of P, G, and Y.
//
if(!CryptGetKeyParam(hServerDhKey, KP_P, NULL, &cbP, 0))
{
SP_LOG_RESULT(GetLastError());
pctRet = PCT_INT_INTERNAL_ERROR;
goto cleanup;
}
if(!CryptGetKeyParam(hServerDhKey, KP_G, NULL, &cbG, 0))
{
SP_LOG_RESULT(GetLastError());
pctRet = PCT_INT_INTERNAL_ERROR;
goto cleanup;
}
if(!CryptExportKey(hServerDhKey,
0,
PUBLICKEYBLOB,
0,
NULL,
&cbY))
{
SP_LOG_RESULT(GetLastError());
pctRet = PCT_INT_INTERNAL_ERROR;
goto cleanup;
}
//
// Compute approximate size of ServerKeyExchange message.
//
cbMessage = 2 + cbP +
2 + cbG +
2 + cbY + sizeof(DWORD) +
2 + MAX_DSA_ENCODED_SIGNATURE_SIZE;
if(pServerExchangeValue == NULL)
{
*pcbServerExchangeValue = cbMessage;
pctRet = PCT_ERR_OK;
goto cleanup;
}
if(*pcbServerExchangeValue < cbMessage)
{
*pcbServerExchangeValue = cbMessage;
pctRet = SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
goto cleanup;
}
//
// Build the ServerDHParams structure.
//
pbMessage = pServerExchangeValue;
cbBytesLeft = cbMessage;
// Get P.
if(!CryptGetKeyParam(hServerDhKey, KP_P, pbMessage + 2, &cbP, 0))
{
SP_LOG_RESULT(GetLastError());
pctRet = PCT_INT_INTERNAL_ERROR;
goto cleanup;
}
ReverseInPlace(pbMessage + 2, cbP);
pbMessage[0] = MSBOF(cbP);
pbMessage[1] = LSBOF(cbP);
pbMessage += 2 + cbP;
cbBytesLeft -= 2 + cbP;
// Get G.
if(!CryptGetKeyParam(hServerDhKey, KP_G, pbMessage + 2, &cbG, 0))
{
SP_LOG_RESULT(GetLastError());
pctRet = PCT_INT_INTERNAL_ERROR;
goto cleanup;
}
ReverseInPlace(pbMessage + 2, cbG);
pbMessage[0] = MSBOF(cbG);
pbMessage[1] = LSBOF(cbG);
pbMessage += 2 + cbG;
cbBytesLeft -= 2 + cbG;
// Get Ys.
{
BLOBHEADER *pBlobHeader;
DHPUBKEY * pDHPubKey;
PBYTE pbKey;
DWORD cbKey;
pBlobHeader = (BLOBHEADER *)ROUND_UP_POINTER(pbMessage, ALIGN_DWORD);
cbData = cbBytesLeft - sizeof(DWORD);
if(!CryptExportKey(hServerDhKey,
0,
PUBLICKEYBLOB,
0,
(PBYTE)pBlobHeader,
&cbData))
{
SP_LOG_RESULT(GetLastError());
pctRet = PCT_INT_INTERNAL_ERROR;
goto cleanup;
}
pDHPubKey = (DHPUBKEY *)(pBlobHeader + 1);
pbKey = (BYTE *)(pDHPubKey + 1);
cbKey = pDHPubKey->bitlen / 8;
if(pDHPubKey->bitlen % 8) cbKey++;
MoveMemory(pbMessage + 2, pbKey, cbKey);
ReverseInPlace(pbMessage + 2, cbKey);
pbMessage[0] = MSBOF(cbKey);
pbMessage[1] = LSBOF(cbKey);
pbMessage += 2 + cbKey;
cbBytesLeft -= 2 + cbKey;
}
//
// Sign the ServerDHParams structure.
//
cbSignature = cbBytesLeft - 2;
pctRet = SPSignDssParams(pContext,
pCred,
pServerExchangeValue,
(DWORD)(pbMessage - pServerExchangeValue),
pbMessage + 2,
&cbSignature);
if(pctRet != PCT_ERR_OK)
{
SP_LOG_RESULT(pctRet);
goto cleanup;
}
pbMessage[0] = MSBOF(cbSignature);
pbMessage[1] = LSBOF(cbSignature);
pbMessage += 2 + cbSignature;
cbBytesLeft -= 2 + cbSignature;
//
// Update function outputs.
//
SP_ASSERT(cbBytesLeft < cbMessage);
*pcbServerExchangeValue = (DWORD)(pbMessage - pServerExchangeValue);
// Use ephemeral key for the new connection.
pContext->RipeZombie->hMasterProv = hProv;
pContext->RipeZombie->dwCapiFlags = SCH_CAPI_USE_CSP;
pctRet = PCT_ERR_OK;
cleanup:
if(fImpersonating)
{
RevertToSelf();
}
return pctRet;
}
SP_STATUS
ParseServerKeyExchange(
PSPContext pContext, // in
PBYTE pbMessage, // in
DWORD cbMessage, // in
PBYTE * ppbServerP, // out
PDWORD pcbServerP, // out
PBYTE * ppbServerG, // out
PDWORD pcbServerG, // out
PBYTE * ppbServerY, // out
PDWORD pcbServerY, // out
BOOL fValidateSig) // in
{
PBYTE pbData;
BLOBHEADER *pPublicBlob;
DWORD cbPublicBlob;
HCRYPTKEY hServerPublic = 0;
PBYTE pbSignature;
DWORD cbSignature;
DWORD cbSignedData;
SP_STATUS pctRet;
//
// Parse out ServerKeyExchange message fields
//
pbData = pbMessage;
*pcbServerP = MAKEWORD(pbData[1], pbData[0]);
*ppbServerP = pbData + 2;
pbData += 2 + *pcbServerP;
if(pbData >= pbMessage + cbMessage)
{
return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
}
*pcbServerG = MAKEWORD(pbData[1], pbData[0]);
*ppbServerG = pbData + 2;
pbData += 2 + *pcbServerG;
if(pbData >= pbMessage + cbMessage)
{
return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
}
*pcbServerY = MAKEWORD(pbData[1], pbData[0]);
*ppbServerY = pbData + 2;
pbData += 2 + *pcbServerY;
if(pbData >= pbMessage + cbMessage)
{
return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
}
cbSignedData = (DWORD)(pbData - pbMessage);
cbSignature = MAKEWORD(pbData[1], pbData[0]);
pbSignature = pbData + 2;
pbData += 2 + cbSignature;
if(pbData != pbMessage + cbMessage)
{
return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
}
if(fValidateSig == FALSE)
{
return PCT_ERR_OK;
}
//
// Validate signature.
//
pPublicBlob = pContext->RipeZombie->pRemotePublic->pPublic;
cbPublicBlob = pContext->RipeZombie->pRemotePublic->cbPublic;
if(!SchCryptImportKey(pContext->RipeZombie->hMasterProv,
(PBYTE)pPublicBlob,
cbPublicBlob,
0,
0,
&hServerPublic,
pContext->RipeZombie->dwCapiFlags))
{
return SP_LOG_RESULT(GetLastError());
}
pctRet = SPVerifyDssParams(
pContext,
pContext->RipeZombie->hMasterProv,
hServerPublic,
pContext->RipeZombie->dwCapiFlags,
pbMessage,
cbSignedData,
pbSignature,
cbSignature);
if(pctRet != PCT_ERR_OK)
{
SchCryptDestroyKey(hServerPublic, pContext->RipeZombie->dwCapiFlags);
return SP_LOG_RESULT(pctRet);
}
SchCryptDestroyKey(hServerPublic, pContext->RipeZombie->dwCapiFlags);
return PCT_ERR_OK;
}
//+---------------------------------------------------------------------------
//
// Function: DHGenerateClientExchangeValue
//
// Synopsis: Create a ClientKeyExchange message, containing an ephemeral
// DH key.
//
// Arguments:
//
// History: 03-24-98 jbanes Added CAPI integration.
//
// Notes: The following data is placed in the output buffer by
// this routine:
//
// struct {
// opaque dh_Yc<1..2^16-1>;
// } ClientDiffieHellmanPublic;
//
//----------------------------------------------------------------------------
SP_STATUS
WINAPI
DHGenerateClientExchangeValue(
SPContext * pContext, // in
PUCHAR pServerExchangeValue, // in
DWORD cbServerExchangeValue, // in
PUCHAR pClientClearValue, // out
DWORD * pcbClientClearValue, // in/out
PUCHAR pClientExchangeValue, // out
DWORD * pcbClientExchangeValue) // in/out
{
HCRYPTKEY hClientDHKey = 0;
PSessCacheItem pZombie;
CRYPT_DATA_BLOB Data;
ALG_ID Algid;
DWORD cbHeader;
SP_STATUS pctRet;
PBYTE pbServerP = NULL;
DWORD cbServerP;
PBYTE pbServerG = NULL;
DWORD cbServerG;
PBYTE pbServerY = NULL;
DWORD cbServerY;
PBYTE pbClientY = NULL;
DWORD cbClientY;
PBYTE pbBlob = NULL;
DWORD cbBlob;
DWORD cbData;
DWORD dwKeySize;
pZombie = pContext->RipeZombie;
if(pZombie == NULL)
{
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
}
if(pZombie->hMasterProv == 0)
{
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
}
// We're doing a full handshake.
pContext->Flags |= CONTEXT_FLAG_FULL_HANDSHAKE;
if(pZombie->fProtocol == SP_PROT_SSL3_CLIENT)
{
Algid = CALG_SSL3_MASTER;
}
else if(pZombie->fProtocol == SP_PROT_TLS1_CLIENT)
{
Algid = CALG_TLS1_MASTER;
}
else
{
return SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH);
}
if(pServerExchangeValue == NULL)
{
return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
}
//
// Is the output buffer large enough?
//
pctRet = ParseServerKeyExchange(pContext,
pServerExchangeValue,
cbServerExchangeValue,
&pbServerP,
&cbServerP,
&pbServerG,
&cbServerG,
&pbServerY,
&cbServerY,
FALSE);
if(pctRet != PCT_ERR_OK)
{
return pctRet;
}
cbBlob = sizeof(BLOBHEADER) + sizeof(DHPUBKEY) + cbServerY + 20;
if(pClientExchangeValue == NULL)
{
*pcbClientExchangeValue = cbBlob;
return PCT_ERR_OK;
}
if(*pcbClientExchangeValue < cbBlob)
{
*pcbClientExchangeValue = cbBlob;
return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
}
//
// Parse the ServerKeyExchange message.
//
pctRet = ParseServerKeyExchange(pContext,
pServerExchangeValue,
cbServerExchangeValue,
&pbServerP,
&cbServerP,
&pbServerG,
&cbServerG,
&pbServerY,
&cbServerY,
TRUE);
if(pctRet != PCT_ERR_OK)
{
return pctRet;
}
//
// Create buffer to use for endian-izing data.
//
cbBlob = sizeof(BLOBHEADER) + sizeof(DHPUBKEY) + cbServerY;
cbBlob = max(cbBlob, cbServerP);
cbBlob = max(cbBlob, cbServerG);
pbBlob = SPExternalAlloc(cbBlob);
if(pbBlob == NULL)
{
pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
goto cleanup;
}
//
// Generate and set the parameters on the client DH key.
//
dwKeySize = cbServerP * 8;
if(!SchCryptGenKey(pZombie->hMasterProv,
CALG_DH_EPHEM,
(dwKeySize << 16) | CRYPT_PREGEN,
&hClientDHKey,
pZombie->dwCapiFlags))
{
pctRet = SP_LOG_RESULT(GetLastError());
goto cleanup;
}
ReverseMemCopy(pbBlob, pbServerP, cbServerP);
Data.pbData = pbBlob;
Data.cbData = cbServerP;
if(!SchCryptSetKeyParam(hClientDHKey,
KP_P,
(PBYTE)&Data,
0,
pZombie->dwCapiFlags))
{
pctRet = SP_LOG_RESULT(GetLastError());
goto cleanup;
}
ReverseMemCopy(pbBlob, pbServerG, cbServerG);
Data.pbData = pbBlob;
Data.cbData = cbServerG;
if(cbServerG < cbServerP)
{
// Expand G so that it's the same size as P.
ZeroMemory(pbBlob + cbServerG, cbServerP - cbServerG);
Data.cbData = cbServerP;
}
if(!SchCryptSetKeyParam(hClientDHKey,
KP_G,
(PBYTE)&Data,
0,
pZombie->dwCapiFlags))
{
pctRet = SP_LOG_RESULT(GetLastError());
goto cleanup;
}
// actually create the client private DH key
if(!SchCryptSetKeyParam(hClientDHKey,
KP_X,
NULL,
0,
pZombie->dwCapiFlags))
{
pctRet = SP_LOG_RESULT(GetLastError());
goto cleanup;
}
//
// Import the server's public key and generate the master secret.
//
{
BLOBHEADER * pBlobHeader;
DHPUBKEY * pDHPubKey;
PBYTE pbKey;
// Build PUBLICKEYBLOB around the server's public key.
pBlobHeader = (BLOBHEADER *)pbBlob;
pDHPubKey = (DHPUBKEY *)(pBlobHeader + 1);
pbKey = (PBYTE)(pDHPubKey + 1);
pBlobHeader->bType = PUBLICKEYBLOB;
pBlobHeader->bVersion = CUR_BLOB_VERSION;
pBlobHeader->reserved = 0;
pBlobHeader->aiKeyAlg = CALG_DH_EPHEM;
pDHPubKey->magic = MAGIC_DH1;
pDHPubKey->bitlen = cbServerY * 8;
ReverseMemCopy(pbKey, pbServerY, cbServerY);
if(!SchCryptImportKey(pZombie->hMasterProv,
pbBlob,
cbBlob,
hClientDHKey,
0,
&pZombie->hMasterKey,
pZombie->dwCapiFlags))
{
pctRet = GetLastError();
goto cleanup;
}
}
// Determine size of key exchange key.
cbData = sizeof(DWORD);
if(!SchCryptGetKeyParam(hClientDHKey,
KP_BLOCKLEN,
(PBYTE)&pZombie->dwExchStrength,
&cbData,
0,
pContext->RipeZombie->dwCapiFlags))
{
SP_LOG_RESULT(GetLastError());
pContext->RipeZombie->dwExchStrength = 0;
}
//
// Convert the agreed key to the appropriate master key type.
//
if(!SchCryptSetKeyParam(pZombie->hMasterKey,
KP_ALGID,
(PBYTE)&Algid,
0,
pZombie->dwCapiFlags))
{
pctRet = SP_LOG_RESULT(GetLastError());
goto cleanup;
}
//
// Export the client public key, strip off the blob header
// goo and attach a two byte length field. This will make up our
// ClientKeyExchange message.
//
if(!SchCryptExportKey(hClientDHKey,
0,
PUBLICKEYBLOB,
0,
pClientExchangeValue,
pcbClientExchangeValue,
pZombie->dwCapiFlags))
{
pctRet = SP_LOG_RESULT(GetLastError());
goto cleanup;
}
cbHeader = sizeof(BLOBHEADER) + sizeof(DHPUBKEY);
cbClientY = *pcbClientExchangeValue - cbHeader;
pbClientY = pClientExchangeValue + cbHeader;
pClientExchangeValue[0] = MSBOF(cbClientY);
pClientExchangeValue[1] = LSBOF(cbClientY);
ReverseInPlace(pbClientY, cbClientY);
MoveMemory(pClientExchangeValue + 2, pbClientY, cbClientY);
*pcbClientExchangeValue = 2 + cbClientY;
//
// Build the session keys.
//
pctRet = MakeSessionKeys(pContext,
pZombie->hMasterProv,
pZombie->hMasterKey);
if(pctRet != PCT_ERR_OK)
{
goto cleanup;
}
// Update perf counter.
InterlockedIncrement(&g_cClientHandshakes);
pctRet = PCT_ERR_OK;
cleanup:
if(pbBlob)
{
SPExternalFree(pbBlob);
}
if(hClientDHKey)
{
SchCryptDestroyKey(hClientDHKey, pZombie->dwCapiFlags);
}
return pctRet;
}
//+---------------------------------------------------------------------------
//
// 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] --
// [cbClientExchangeValue] --
//
// History: 03-25-98 jbanes Created.
//
// Notes: The following data is supposed to be in the input buffer:
//
// struct {
// opaque dh_Yc<1..2^16-1>;
// } ClientDiffieHellmanPublic;
//
//----------------------------------------------------------------------------
SP_STATUS
WINAPI
DHGenerateServerMasterKey(
SPContext * pContext, // in
PUCHAR pClientClearValue, // in
DWORD cbClientClearValue, // in
PUCHAR pClientExchangeValue, // in
DWORD cbClientExchangeValue) // in
{
PSessCacheItem pZombie;
ALG_ID Algid;
SP_STATUS pctRet;
PBYTE pbClientY;
DWORD cbClientY;
HCRYPTKEY hTek;
BOOL fImpersonating = FALSE;
pZombie = pContext->RipeZombie;
if(pZombie == NULL)
{
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
}
if(pZombie->hMasterProv == 0)
{
return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
}
// We're doing a full handshake.
pContext->Flags |= CONTEXT_FLAG_FULL_HANDSHAKE;
fImpersonating = SslImpersonateClient();
pctRet = GetDHEphemKey(pContext,
NULL,
&hTek);
if(pctRet != PCT_ERR_OK)
{
SP_LOG_RESULT(pctRet);
goto cleanup;
}
if(pZombie->fProtocol == SP_PROT_SSL3_SERVER)
{
Algid = CALG_SSL3_MASTER;
}
else if(pZombie->fProtocol == SP_PROT_TLS1_SERVER)
{
Algid = CALG_TLS1_MASTER;
}
else
{
pctRet = SP_LOG_RESULT(PCT_INT_SPECS_MISMATCH);
goto cleanup;
}
//
// Parse ClientKeyExchange message.
//
if(pClientExchangeValue == NULL || cbClientExchangeValue <= 2)
{
pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
goto cleanup;
}
cbClientY = MAKEWORD(pClientExchangeValue[1], pClientExchangeValue[0]);
pbClientY = pClientExchangeValue + 2;
if(2 + cbClientY != cbClientExchangeValue)
{
pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG);
goto cleanup;
}
//
// Import the client's public key and generate the master secret.
//
{
BLOBHEADER * pBlobHeader;
DHPUBKEY * pDHPubKey;
PBYTE pbKey;
PBYTE pbBlob;
DWORD cbBlob;
// Build PUBLICKEYBLOB around the server's public key.
cbBlob = sizeof(BLOBHEADER) + sizeof(DHPUBKEY) + cbClientY;
pbBlob = SPExternalAlloc(cbBlob);
if(pbBlob == NULL)
{
pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
goto cleanup;
}
pBlobHeader = (BLOBHEADER *)pbBlob;
pDHPubKey = (DHPUBKEY *)(pBlobHeader + 1);
pbKey = (PBYTE)(pDHPubKey + 1);
pBlobHeader->bType = PUBLICKEYBLOB;
pBlobHeader->bVersion = CUR_BLOB_VERSION;
pBlobHeader->reserved = 0;
pBlobHeader->aiKeyAlg = CALG_DH_EPHEM;
pDHPubKey->magic = MAGIC_DH1;
pDHPubKey->bitlen = cbClientY * 8;
ReverseMemCopy(pbKey, pbClientY, cbClientY);
if(!SchCryptImportKey(pZombie->hMasterProv,
pbBlob,
cbBlob,
hTek,
0,
&pZombie->hMasterKey,
pZombie->dwCapiFlags))
{
pctRet = GetLastError();
SPExternalFree(pbBlob);
goto cleanup;
}
SPExternalFree(pbBlob);
}
//
// Convert the agreed key to the appropriate master key type.
//
if(!SchCryptSetKeyParam(pZombie->hMasterKey,
KP_ALGID, (PBYTE)&Algid,
0,
pZombie->dwCapiFlags))
{
pctRet = SP_LOG_RESULT(GetLastError());
goto cleanup;
}
//
// Build the session keys.
//
pctRet = MakeSessionKeys(pContext,
pZombie->hMasterProv,
pZombie->hMasterKey);
if(pctRet != PCT_ERR_OK)
{
goto cleanup;
}
// Update perf counter.
InterlockedIncrement(&g_cServerHandshakes);
pctRet = PCT_ERR_OK;
cleanup:
if(fImpersonating)
{
RevertToSelf();
}
return pctRet;
}
void
ReverseInPlace(PUCHAR pByte, DWORD cbByte)
{
DWORD i;
BYTE bSave;
for(i=0; i< cbByte/2; i++)
{
bSave = pByte[i];
pByte[i] = pByte[cbByte-i-1];
pByte[cbByte-i-1] = bSave;
}
}