|
|
//+---------------------------------------------------------------------------
//
// 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; while(p >= Dest) { *p-- = *Source++; } }
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); }
LockCredentialExclusive(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)) { SafeAllocaAllocate(pProvInfo, 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())); SafeAllocaFree(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(!CryptAcquireContextW(phEphemProv, NULL, pProvInfo->pwszProvName, pProvInfo->dwProvType, CRYPT_VERIFYCONTEXT)) { SP_LOG_RESULT(GetLastError()); pctRet = SEC_E_NO_CREDENTIALS; goto cleanup; }
SafeAllocaFree(pProvInfo); pProvInfo = NULL; } else { if(!CryptAcquireContextW(phEphemProv, NULL, NULL, PROV_RSA_SCHANNEL, CRYPT_VERIFYCONTEXT)) { SP_LOG_RESULT(GetLastError()); pctRet = SEC_E_NO_CREDENTIALS; goto cleanup; } } }
//
// Obtain handle to private key.
//
if(!CryptGetUserKey(*phEphemProv, AT_KEYEXCHANGE, phEphemeralKey)) { // Key does not exist, so attempt to create one.
DebugLog((DEB_TRACE, "Creating %d-bit ephemeral key.\n", dwKeySize)); if(!CryptGenKey(*phEphemProv, AT_KEYEXCHANGE, (dwKeySize << 16), phEphemeralKey)) { 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) { SafeAllocaFree(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(!CryptGetUserKey(pCred->hProv, pCred->dwKeySpec, &hServerKey)) { 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(!CryptGetKeyParam(hServerKey, KP_BLOCKLEN, (PBYTE)&cbServerModulus, &cbData, 0)) { SP_LOG_RESULT(GetLastError()); CryptDestroyKey(hServerKey); pctRet = PCT_INT_INTERNAL_ERROR; goto cleanup; } CryptDestroyKey(hServerKey);
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(!CryptExportKey(hEphemeralKey, 0, PUBLICKEYBLOB, 0, NULL, &cbBlob)) { SP_LOG_RESULT(GetLastError()); pctRet = PCT_INT_INTERNAL_ERROR; goto cleanup; } SafeAllocaAllocate(pbBlob, cbBlob); if(pbBlob == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } if(!CryptExportKey(hEphemeralKey, 0, PUBLICKEYBLOB, 0, pbBlob, &cbBlob)) { SP_LOG_RESULT(GetLastError()); SafeAllocaFree(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.
CryptDestroyKey(hEphemeralKey);
//
// 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;
SafeAllocaFree(pbBlob); pbBlob = NULL;
fImpersonating = SslImpersonateClient();
// Generate hash values
ComputeServerExchangeHashes( pContext, pServerExchangeValue, (int)(pbMessage - pServerExchangeValue), rgbHashValue, rgbHashValue + CB_MD5_DIGEST_LEN);
// Sign hash value.
if(!CryptCreateHash(pCred->hProv, CALG_SSL3_SHAMD5, 0, 0, &hHash)) { SP_LOG_RESULT(GetLastError()); pctRet = PCT_INT_INTERNAL_ERROR; goto cleanup; } if(!CryptSetHashParam(hHash, HP_HASHVAL, rgbHashValue, 0)) { SP_LOG_RESULT(GetLastError()); pctRet = PCT_INT_INTERNAL_ERROR; goto cleanup; }
DebugLog((DEB_TRACE, "Signing server_key_exchange message.\n")); cbSignature = cbServerModulus; if(!CryptSignHash(hHash, pCred->dwKeySpec, NULL, 0, pbMessage + 2, &cbSignature)) { SP_LOG_RESULT(GetLastError()); pctRet = PCT_INT_INTERNAL_ERROR; goto cleanup; } DebugLog((DEB_TRACE, "Server_key_exchange message signed successfully.\n")); CryptDestroyHash(hHash);
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(!CryptImportKey(pContext->RipeZombie->hMasterProv, (PBYTE)pPublicBlob, cbPublicBlob, 0, 0, &hServerPublic)) { 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; pctRet = GenerateRandomBits(pContext->RipeZombie->pClearKey, pContext->RipeZombie->cbClearKey); if(!NT_SUCCESS(pctRet)) { goto done; }
*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) { pctRet = GenerateRandomBits(pContext->RipeZombie->pClearKey, pContext->RipeZombie->cbClearKey); if(!NT_SUCCESS(pctRet)) { goto done; }
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.
CryptDestroyKey(hServerPublic);
// Use public key from ServerKeyExchange instead.
hServerPublic = hNewServerPublic; }
break;
default: return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
// Generate the master_secret.
if(!CryptGenKey(pContext->RipeZombie->hMasterProv, Algid, dwGenFlags, &pContext->RipeZombie->hMasterKey)) { 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(!CryptSetKeyParam(pContext->RipeZombie->hMasterKey, KP_HIGHEST_VERSION, (PBYTE)&dwHighestProtocol, 0)) { SP_LOG_RESULT(GetLastError()); } #endif
// Encrypt the master_secret.
DebugLog((DEB_TRACE, "Encrypt the master secret.\n")); if(!CryptExportKey(pContext->RipeZombie->hMasterKey, hServerPublic, SIMPLEBLOB, dwExportFlags, pClientExchangeValue, &cbEncryptedKey)) { 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(!CryptGetKeyParam(hServerPublic, KP_BLOCKLEN, (PBYTE)&pContext->RipeZombie->dwExchStrength, &cbData, 0)) { 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) CryptDestroyKey(hServerPublic);
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(!CryptGenKey(pContext->RipeZombie->hMasterProv, Algid, dwGenFlags, phMasterKey)) { 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; DWORD cbExpectedLength;
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;
if(cbClientClearValue > sizeof(pContext->RipeZombie->pClearKey)) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); } 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; }
if(cbClientClearValue > sizeof(pContext->RipeZombie->pClearKey)) { return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); } 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); }
// Get handle to key exchange key.
if(!CryptGetUserKey(pContext->RipeZombie->hMasterProv, AT_KEYEXCHANGE, &hExchKey)) { SP_LOG_RESULT(GetLastError()); pctRet = PCT_INT_INTERNAL_ERROR; goto cleanup; }
// Determine size of key exchange key.
cbData = sizeof(DWORD); if(!CryptGetKeyParam(hExchKey, KP_BLOCKLEN, (PBYTE)&pContext->RipeZombie->dwExchStrength, &cbData, 0)) { SP_LOG_RESULT(GetLastError()); pContext->RipeZombie->dwExchStrength = 0; } cbExpectedLength = (pContext->RipeZombie->dwExchStrength + 7) / 8;
if(cbClientExchangeValue < 2) { pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG); goto cleanup; }
// 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 == cbExpectedLength) && (cbMsg + 2 == cbClientExchangeValue)) { pClientExchangeValue += 2; cbClientExchangeValue -= 2; } }
if(cbClientExchangeValue < cbExpectedLength) { pctRet = SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG); goto cleanup; }
// Allocate memory for blob.
cbBlob = sizeof(BLOBHEADER) + sizeof(ALG_ID) + cbClientExchangeValue; SafeAllocaAllocate(pbBlob, 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(!CryptImportKey(pContext->RipeZombie->hMasterProv, pbBlob, cbBlob, hExchKey, dwFlags, &hMasterKey)) { 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(!CryptSetKeyParam(hMasterKey, KP_HIGHEST_VERSION, (PBYTE)&dwHighestProtocol, CRYPT_SERVER)) { SP_LOG_RESULT(GetLastError());
if(GetLastError() == NTE_BAD_VER) { pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); CryptDestroyKey(hMasterKey); goto cleanup; } } }
pContext->RipeZombie->hMasterKey = hMasterKey;
CryptDestroyKey(hExchKey); 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) { SafeAllocaFree(pbBlob); }
if(hExchKey) { CryptDestroyKey(hExchKey); }
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(!CryptSetKeyParam(hMasterKey, KP_SCHANNEL_ALG, (PBYTE)&Algorithm, 0)) { 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(!CryptSetKeyParam(hMasterKey, KP_SCHANNEL_ALG, (PBYTE)&Algorithm, 0)) { 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(!CryptSetKeyParam(hMasterKey, KP_CLEAR_KEY, (BYTE*)&Data, 0)) { SP_LOG_RESULT(GetLastError()); return PCT_INT_INTERNAL_ERROR; } }
// Specify the CH_CHALLENGE_DATA.
Data.pbData = pContext->pChallenge; Data.cbData = pContext->cbChallenge; if(!CryptSetKeyParam(hMasterKey, KP_CLIENT_RANDOM, (BYTE*)&Data, 0)) { SP_LOG_RESULT(GetLastError()); return PCT_INT_INTERNAL_ERROR; }
// Specify the SH_CONNECTION_ID_DATA.
Data.pbData = pContext->pConnectionID; Data.cbData = pContext->cbConnectionID; if(!CryptSetKeyParam(hMasterKey, KP_SERVER_RANDOM, (BYTE*)&Data, 0)) { 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(!CryptSetKeyParam(hMasterKey, KP_CERTIFICATE, (BYTE*)&Data, 0)) { 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(!CryptSetKeyParam(hMasterKey, KP_CLEAR_KEY, (BYTE*)&Data, 0)) { SP_LOG_RESULT(GetLastError()); return PCT_INT_INTERNAL_ERROR; } }
// Specify the CH_CHALLENGE_DATA.
Data.pbData = pContext->pChallenge; Data.cbData = pContext->cbChallenge; if(!CryptSetKeyParam(hMasterKey, KP_CLIENT_RANDOM, (BYTE*)&Data, 0)) { SP_LOG_RESULT(GetLastError()); return PCT_INT_INTERNAL_ERROR; }
// Specify the SH_CONNECTION_ID_DATA.
Data.pbData = pContext->pConnectionID; Data.cbData = pContext->cbConnectionID; if(!CryptSetKeyParam(hMasterKey, KP_SERVER_RANDOM, (BYTE*)&Data, 0)) { 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(!CryptSetKeyParam(hMasterKey, KP_CLIENT_RANDOM, (BYTE*)&Data, 0)) { SP_LOG_RESULT(GetLastError()); return PCT_INT_INTERNAL_ERROR; }
// Specify server_random.
Data.pbData = pContext->rgbS3SRandom; Data.cbData = CB_SSL3_RANDOM; if(!CryptSetKeyParam(hMasterKey, KP_SERVER_RANDOM, (BYTE*)&Data, 0)) { 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(!CryptCreateHash(hProv, CALG_SCHANNEL_MASTER_HASH, hMasterKey, 0, &hMasterHash)) { pctRet = SP_LOG_RESULT(GetLastError()); goto cleanup; }
// Derive read key from the master hash object.
if(pContext->hPendingReadKey) { CryptDestroyKey(pContext->hPendingReadKey); pContext->hPendingReadKey = 0; } if(pContext->pPendingCipherInfo->aiCipher != CALG_NULLCIPHER) { if(!CryptDeriveKey(hProv, CALG_SCHANNEL_ENC_KEY, hMasterHash, CRYPT_EXPORTABLE | (fClient ? CRYPT_SERVER : 0), &pContext->hPendingReadKey)) { pctRet = SP_LOG_RESULT(GetLastError()); goto cleanup; } }
// Derive write key from the master hash object.
if(pContext->hPendingWriteKey) { CryptDestroyKey(pContext->hPendingWriteKey); pContext->hPendingWriteKey = 0; } if(pContext->pPendingCipherInfo->aiCipher != CALG_NULLCIPHER) { if(!CryptDeriveKey(hProv, CALG_SCHANNEL_ENC_KEY, hMasterHash, CRYPT_EXPORTABLE | (fClient ? 0 : CRYPT_SERVER), &pContext->hPendingWriteKey)) { 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(!CryptSetKeyParam(pContext->hPendingReadKey, KP_IV, pContext->RipeZombie->pKeyArgs, 0)) { pctRet = SP_LOG_RESULT(GetLastError()); goto cleanup; }
if(!CryptSetKeyParam(pContext->hPendingWriteKey, KP_IV, pContext->RipeZombie->pKeyArgs, 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) { CryptDestroyKey(pContext->hPendingReadMAC); } if(!CryptDeriveKey(hProv, CALG_SCHANNEL_MAC_KEY, hMasterHash, CRYPT_EXPORTABLE | (fClient ? CRYPT_SERVER : 0), &pContext->hPendingReadMAC)) { pctRet = SP_LOG_RESULT(GetLastError()); goto cleanup; }
// Derive write MAC from the master hash object.
if(pContext->hPendingWriteMAC) { CryptDestroyKey(pContext->hPendingWriteMAC); } if(!CryptDeriveKey(hProv, CALG_SCHANNEL_MAC_KEY, hMasterHash, CRYPT_EXPORTABLE | (fClient ? 0 : CRYPT_SERVER), &pContext->hPendingWriteMAC)) { pctRet = SP_LOG_RESULT(GetLastError()); goto cleanup; } }
pctRet = PCT_ERR_OK;
cleanup:
if(hMasterHash) { CryptDestroyHash(hMasterHash); }
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; 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;
if(cbMessage < 3) { return SP_LOG_RESULT(PCT_INT_ILLEGAL_MSG); }
// 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; SafeAllocaAllocate(pbLocalBuffer, 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(!CryptCreateHash(pContext->RipeZombie->hMasterProv, CALG_SSL3_SHAMD5, 0, 0, &hHash)) { SP_LOG_RESULT(GetLastError()); SafeAllocaFree(pbLocalBuffer); return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
// set hash value
if(!CryptSetHashParam(hHash, HP_HASHVAL, rgbHashValue, 0)) { SP_LOG_RESULT(GetLastError()); CryptDestroyHash(hHash); SafeAllocaFree(pbLocalBuffer); return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
DebugLog((DEB_TRACE, "Verify server_key_exchange message signature.\n")); if(!CryptVerifySignature(hHash, pbLocalBuffer, cbSignature, hServerPublic, NULL, 0)) { DebugLog((DEB_WARN, "Signature Verify Failed: %x\n", GetLastError())); CryptDestroyHash(hHash); SafeAllocaFree(pbLocalBuffer); return SP_LOG_RESULT(PCT_ERR_INTEGRITY_CHECK_FAILED); } DebugLog((DEB_TRACE, "Server_key_exchange message signature verified okay.\n"));
CryptDestroyHash(hHash); SafeAllocaFree(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; SafeAllocaAllocate(pbBlob, 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(!CryptImportKey(pContext->RipeZombie->hMasterProv, pbBlob, cbBlob, 0, 0, phNewServerPublic)) { SP_LOG_RESULT(GetLastError()); SafeAllocaFree(pbBlob); return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); }
SafeAllocaFree(pbBlob); }
return PCT_ERR_OK; }
|