//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995. // // File: keyxmsdh.c // // Contents: // // Classes: // // Functions: // // History: 10-21-97 jbanes CAPI integration stuff. // //---------------------------------------------------------------------------- #include #include // 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; } }