1228 lines
31 KiB
C
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;
|
|
}
|
|
}
|