#include "pch.hxx" #include "demand.h" #include #include "resource.h" #include "dllmain.h" #ifdef WIN16 #define CRYPT_ACQUIRE_CONTEXT CryptAcquireContextA #else #define CRYPT_ACQUIRE_CONTEXT CryptAcquireContextW #endif //////////////////////////////////////////////////////////////////////////////// const BYTE RgbRc2_40bit[] = {0x2, 0x01, 40}; const BYTE RgbRc2_64bit[] = {0x2, 0x01, 64}; const BYTE RgbRc2_128bit[] = {0x2, 0x02, 0, 128}; const char SzRc2_128[] = "RC2 (128-bit)"; const char SzRc2_64[] = "RC2 (64-bit)"; const char SzRc2_40[] = "RC2 (40-bit)"; const char SzRc2[] = "RC2"; const char SzDES[] = "DES"; const char Sz3DES[] = "3DES"; const char SzSHA1[] = "SHA1"; const char SzSHA_1[] = "SHA-1"; const char SzMD5[] = "MD5"; const char SzSkipjack[] = "SKIPJACK"; static char RgchUnknown[256]; static char Rgch[256]; // encryption bits const DWORD cdwBits_3DES = 3 * 56; const DWORD cdwBits_RC2_128bit = 128; const DWORD cdwBits_RC2_64bit = 64; const DWORD cdwBits_DES = 56; const DWORD cdwBits_RC2_40bit = 40; // signing const DWORD cdwBits_SHA1RSA = 160; const DWORD cdwBits_OIWSEC_sha1 = 160; const DWORD cdwBits_MD5 = 128; #define flEncryption 1 #define flSigning 2 #define flOther 3 struct { DWORD dwFlags; char * pszObjId; // OID for the alg DWORD cbData; // size of parameters const BYTE * pbData; DWORD dwBits; // size in bits const char * szCSPAlgName; const char * szAlgName; // Name of algorithm } const RgAlgsDesc[] = { {flEncryption, szOID_RSA_DES_EDE3_CBC, 0, NULL, cdwBits_3DES, Sz3DES, Sz3DES}, {flEncryption, szOID_RSA_RC2CBC, sizeof(RgbRc2_128bit), RgbRc2_128bit, cdwBits_RC2_128bit, SzRc2, SzRc2_128}, {flEncryption, szOID_RSA_RC2CBC, sizeof(RgbRc2_64bit), RgbRc2_64bit, cdwBits_RC2_64bit, SzRc2, SzRc2_64}, {flEncryption, szOID_OIWSEC_desCBC, 0, NULL, cdwBits_DES, SzDES, SzDES}, {flEncryption, szOID_RSA_RC2CBC, sizeof(RgbRc2_40bit), RgbRc2_40bit, cdwBits_RC2_40bit, SzRc2, SzRc2_40}, {flEncryption, szOID_INFOSEC_mosaicConfidentiality, 0, NULL, 80, SzSkipjack, SzSkipjack}, {flSigning, szOID_OIWSEC_sha1, 0, NULL, cdwBits_OIWSEC_sha1,SzSHA_1, SzSHA1}, {flSigning, szOID_RSA_MD5, 0, NULL, cdwBits_MD5, SzMD5, SzMD5}, {flOther, szOID_RSA_preferSignedData, 0, NULL, 0, NULL, NULL} }; const DWORD CEncAlgs = sizeof(RgAlgsDesc)/sizeof(RgAlgsDesc[0]); const int ISignDef = 5; // Must be updated whend RgAlgsDesc modified const int IRC240 = 4; HRESULT GetAlgorithmsFromCert(PCCERT_CONTEXT pcCert, BOOL * rgfShow, ULONG CEncAlgs) { HCRYPTPROV hprov; PCRYPT_KEY_PROV_INFO pkeyinfo = NULL; LPWSTR pwszContainer = NULL; LPWSTR pwszProvName = NULL; // use default provider DWORD dwProvType = PROV_RSA_FULL; HRESULT hr = S_OK; ULONG f; BOOL fRetried = FALSE; ULONG i2; ULONG cb; if (pcCert) { cb = 0; f = CertGetCertificateContextProperty(pcCert, CERT_KEY_PROV_INFO_PROP_ID, NULL, &cb); if (cb) { if (!MemAlloc((LPVOID *) &pkeyinfo, cb)) { hr = E_OUTOFMEMORY; goto err; } f = CertGetCertificateContextProperty(pcCert, CERT_KEY_PROV_INFO_PROP_ID, pkeyinfo, &cb); Assert(f); pwszProvName = pkeyinfo->pwszProvName; dwProvType = pkeyinfo->dwProvType; pwszContainer = pkeyinfo->pwszContainerName; } // else cert doesn't specify provider. Use default provider. } // else use default provider TryEnhanced: f = CRYPT_ACQUIRE_CONTEXT(&hprov, pwszContainer, pwszProvName, dwProvType, 0); #ifdef DEBUG { DWORD dw = GetLastError(); } #endif // DEBUG if (f) { DWORD cbMax; PROV_ENUMALGS * pbData = NULL; cbMax = 0; CryptGetProvParam(hprov, PP_ENUMALGS, NULL, &cbMax, CRYPT_FIRST); if ((cbMax == 0) || !MemAlloc((LPVOID *) &pbData, cbMax)) { hr = E_OUTOFMEMORY; goto err; } cb = cbMax; f = CryptGetProvParam(hprov, PP_ENUMALGS, (LPBYTE)pbData, &cb, CRYPT_FIRST); Assert(f); do { for (i2 = 0; i2 < CEncAlgs - 1; i2++) { if ((strcmp(pbData->szName, RgAlgsDesc[i2].szCSPAlgName) == 0) && (pbData->dwBitLen == RgAlgsDesc[i2].dwBits)) { rgfShow[i2] = TRUE; break; } } cb = cbMax; f = CryptGetProvParam(hprov, PP_ENUMALGS, (LPBYTE) pbData, &cb, 0); } while (f); CryptReleaseContext(hprov, 0); SafeMemFree(pbData); // // Some providers are really crazy, they have a base and an enhanced provider // and these providers do not do the same set of algorithms. THis means // that we need to enumerate all of the different algorithms when we // are looking at these providers. We have the "exhaustive" set of // providers at this point in time. // if (!fRetried) { fRetried = TRUE; #ifndef WIN16 if (! pwszProvName || (StrCmpW(pwszProvName, MS_DEF_PROV_W) == NULL)) { pwszProvName = MS_ENHANCED_PROV_W; goto TryEnhanced; } if (StrCmpW(pwszProvName, MS_DEF_DSS_DH_PROV_W) == 0) { pwszProvName = MS_ENH_DSS_DH_PROV_W; goto TryEnhanced; } if (StrCmpW(pwszProvName, MS_ENHANCED_PROV_W) == NULL) { pwszProvName = MS_DEF_PROV_W; goto TryEnhanced; } if (StrCmpW(pwszProvName, MS_ENH_DSS_DH_PROV_W) == NULL) { pwszProvName = MS_DEF_DSS_DH_PROV_W; goto TryEnhanced; } #else if (! pwszProvName || (wcscmp(pwszProvName, MS_DEF_PROV_A) == NULL)) { pwszProvName = MS_ENHANCED_PROV_A; goto TryEnhanced; } #endif } } SafeMemFree(pkeyinfo); // // If we are looking at diffie-hellman certificates, then we must remove DES // from the list as there is no support in the core code. // if (dwProvType == PROV_DSS_DH) { for (i2=0; i2cCapability; i++) { for (i2=0; i2rgCapability[i].pszObjId, RgAlgsDesc[i2].pszObjId) == 0) && (pcaps->rgCapability[i].Parameters.cbData == RgAlgsDesc[i2].cbData) && (memcmp(pcaps->rgCapability[i].Parameters.pbData, RgAlgsDesc[i2].pbData, RgAlgsDesc[i2].cbData) == 0)) { rgfShow[i2] = TRUE; if ((RgAlgsDesc[i2].dwFlags == flEncryption) && (iEncDef == -1)) { iEncDef = i2; } else if ((RgAlgsDesc[i2].dwFlags == flSigning) && (iSignDef == -1)) { iSignDef = i2; } break; } } if (i2 == CEncAlgs) { pcaps->rgCapability[i].pszObjId = NULL; } } } // // For each certificate, we now want to find the list of capabilities // provided by each of the CSP providers // for (i = 0; i < cCerts; i++) { hr = GetAlgorithmsFromCert(rgCerts[i], rgfShow, CEncAlgs); } // If there were no cert, get the algorithms from the default provider. if (! cCerts) { hr = GetAlgorithmsFromCert(NULL, rgfShow, CEncAlgs); } // // Now populate the combo box with the encryption algrithms if we have // a possiblity todo this. // if (idEncAlgs != 0) { SendDlgItemMessageA(hwnd, idEncAlgs, CB_RESETCONTENT, 0, 0); for (i=0; icCapability; i++) { if ((strcmp(pcaps->rgCapability[i].pszObjId, RgAlgsDesc[i2].pszObjId) == 0) && (pcaps->rgCapability[i].Parameters.cbData == RgAlgsDesc[i2].cbData) && (memcmp(pcaps->rgCapability[i].Parameters.pbData, RgAlgsDesc[i2].pbData, RgAlgsDesc[i2].cbData) == 0)) { break; } } if (i == pcaps->cCapability) { rgbFilter[i2] = FALSE; f = FALSE; } } hr = f ? S_OK : S_FALSE; err: SafeMemFree(pcaps); return hr; } //// SymCapInit // MIMEOLEAPI MimeOleSMimeCapInit(LPBYTE pbSymCapSender, DWORD cbSymCapSender, LPVOID * ppv) { HRESULT hr = S_OK; DWORD i; LPBYTE pb = NULL; if (!MemAlloc((LPVOID *) &pb, CEncAlgs * sizeof(BYTE))) { return E_OUTOFMEMORY; } if (pbSymCapSender && cbSymCapSender) { for (i=0; iszName, RgAlgsDesc[i].szCSPAlgName) == 0) { if (pbData->dwBitLen >= RgAlgsDesc[i].dwBits) { pb[i] = TRUE; // We support this one } } } cb = cbMax; f = CryptGetProvParam(hprov, PP_ENUMALGS, (LPBYTE)pbData, &cb, 0); } while (f); CryptReleaseContext(hprov, 0); SafeMemFree(pbData); // Try the enhanced provider? if (! pszProvName || (lstrcmpi(pszProvName, MS_DEF_PROV) == NULL)) { pszProvName = MS_ENHANCED_PROV; goto TryEnhanced; } } } *ppv = pb; exit: return(hr); } MIMEOLEAPI MimeOleSMimeCapAddSMimeCap(LPBYTE pbSymCap, DWORD cbSymCap, LPVOID pv) { if ((pbSymCap != NULL) && (cbSymCap > 0)) { return SymCapAdd(pbSymCap, cbSymCap, (LPBYTE) pv); } return E_INVALIDARG; } MIMEOLEAPI MimeOleSMimeCapAddCert(LPBYTE /*pbCert*/, DWORD /*cbCert*/, BOOL fParanoid, LPVOID pv) { BOOL f; DWORD i; DWORD iSkip; LPBYTE pb = (LPBYTE) pv; // // If we are paranoid, then we only allow 3-DES // Otherwise we only allow RC2 40-bit // if (fParanoid) { iSkip = 0; } else { iSkip = IRC240; } for (i=0, f = TRUE; icCapability == 1); // // Walk through the list of all known S/MIME caps looking to see if we // have a match. If so then setup the return answer. // for (i=0; irgCapability[0].pszObjId, RgAlgsDesc[i].pszObjId) == 0) && (pcaps->rgCapability[0].Parameters.cbData == RgAlgsDesc[i].cbData) && (memcmp(pcaps->rgCapability[0].Parameters.pbData, RgAlgsDesc[i].pbData, RgAlgsDesc[i].cbData) == 0)) { *ppszProtocol = RgAlgsDesc[i].szAlgName; break; } } // // We did not find a match. So now we need to assume that we might have been // passed a Parameter rather than an S/MIME cap. So try decoding as a parameter // if (i== CEncAlgs) { if (strcmp(pcaps->rgCapability[0].pszObjId, szOID_RSA_RC2CBC) == 0) { PCRYPT_RC2_CBC_PARAMETERS prc2; prc2 = (PCRYPT_RC2_CBC_PARAMETERS) PVDecodeObject(pcaps->rgCapability[0].Parameters.pbData, pcaps->rgCapability[0].Parameters.cbData, PKCS_RC2_CBC_PARAMETERS, NULL); if (prc2 != NULL) { if (prc2->dwVersion == CRYPT_RC2_40BIT_VERSION) { *ppszProtocol = SzRc2_40; } else if (prc2->dwVersion == CRYPT_RC2_64BIT_VERSION) { *ppszProtocol = SzRc2_64; } else if (prc2->dwVersion == CRYPT_RC2_128BIT_VERSION) { *ppszProtocol = SzRc2_128; } else { *ppszProtocol = SzRc2; } SafeMemFree(prc2); // Must be freed prior to pcaps } else { *ppszProtocol = SzRc2; } } else if (strcmp(pcaps->rgCapability[0].pszObjId, szOID_RSA_DES_EDE3_CBC) == 0) { *ppszProtocol = Sz3DES; } else if (strcmp(pcaps->rgCapability[0].pszObjId, szOID_OIWSEC_desCBC) == 0) { *ppszProtocol = SzDES; } else if (strcmp(pcaps->rgCapability[0].pszObjId, szOID_INFOSEC_mosaicConfidentiality) == 0) { *ppszProtocol = SzSkipjack; } else { StrCpyNA(Rgch, pcaps->rgCapability[0].pszObjId, ARRAYSIZE(Rgch)); *ppszProtocol = Rgch; } MemFree(pcaps); return S_FALSE; } MemFree(pcaps); return S_OK; } MIMEOLEAPI MimeOleAlgStrengthFromSMimeCap(LPBYTE pbEncode, DWORD cbEncode, BOOL fEncryption, DWORD * pdwStrength) { DWORD cb = 0; BOOL f; HRESULT hr = S_OK; DWORD i; PCRYPT_SMIME_CAPABILITIES pcaps = NULL; // Init return value *pdwStrength = 0; if (pbEncode && cbEncode) { // // Decode the S/MIME caps which is passed in, allocate space to hold // the resulting value // if ((hr = HrDecodeObject(pbEncode, cbEncode, PKCS_SMIME_CAPABILITIES, CRYPT_DECODE_NOCOPY_FLAG, &cb, (LPVOID *)&pcaps)) || ! pcaps) { goto exit;; } Assert(pcaps); Assert(pcaps->cCapability == 1); // // Walk through the list of all known S/MIME caps looking to see if we // have a match. If so then setup the return answer. // for (i=0; irgCapability[0].pszObjId, RgAlgsDesc[i].pszObjId) == 0) && (pcaps->rgCapability[0].Parameters.cbData == RgAlgsDesc[i].cbData) && (memcmp(pcaps->rgCapability[0].Parameters.pbData, RgAlgsDesc[i].pbData, RgAlgsDesc[i].cbData) == 0)) { *pdwStrength = RgAlgsDesc[i].dwBits; break; } } // // We did not find a match. So now we need to assume that we might have been // passed a Parameter rather than an S/MIME cap. So try decoding as a parameter // if (i== CEncAlgs) { if (strcmp(pcaps->rgCapability[0].pszObjId, szOID_RSA_RC2CBC) == 0) { PCRYPT_RC2_CBC_PARAMETERS prc2; prc2 = (PCRYPT_RC2_CBC_PARAMETERS) PVDecodeObject(pcaps->rgCapability[0].Parameters.pbData, pcaps->rgCapability[0].Parameters.cbData, PKCS_RC2_CBC_PARAMETERS, NULL); if (prc2 != NULL) { if (prc2->dwVersion == CRYPT_RC2_40BIT_VERSION) { *pdwStrength = cdwBits_RC2_40bit; } else if (prc2->dwVersion == CRYPT_RC2_64BIT_VERSION) { *pdwStrength = cdwBits_RC2_64bit; } else if (prc2->dwVersion == CRYPT_RC2_128BIT_VERSION) { *pdwStrength = cdwBits_RC2_128bit; } else { *pdwStrength = cdwBits_RC2_40bit; } SafeMemFree(prc2); // Must be freed prior to pcaps } else { *pdwStrength = cdwBits_RC2_40bit; } } else if (strcmp(pcaps->rgCapability[0].pszObjId, szOID_RSA_DES_EDE3_CBC) == 0) { *pdwStrength = cdwBits_3DES; } else if (strcmp(pcaps->rgCapability[0].pszObjId, szOID_OIWSEC_desCBC) == 0) { *pdwStrength = cdwBits_DES; } else if (strcmp(pcaps->rgCapability[0].pszObjId, szOID_INFOSEC_mosaicConfidentiality) == 0) { *pdwStrength = 80; } else { *pdwStrength = 0; } MemFree(pcaps); return S_FALSE; } MemFree(pcaps); } else { // No SMimeCap passed in, find the maximum supported by this configuration HCRYPTPROV hprov = NULL; LPTSTR pszProvName = NULL; // use default provider DWORD dwProvType = PROV_RSA_FULL; TryEnhanced: f = CryptAcquireContext(&hprov, NULL, pszProvName, dwProvType, CRYPT_VERIFYCONTEXT); if (f) { DWORD cbMax; PROV_ENUMALGS * pbData = NULL; cbMax = 0; CryptGetProvParam(hprov, PP_ENUMALGS, NULL, &cbMax, CRYPT_FIRST); if ((cbMax == 0) || ! MemAlloc((LPVOID *)&pbData, cbMax)) { hr = E_OUTOFMEMORY; goto exit; } cb = cbMax; f = CryptGetProvParam(hprov, PP_ENUMALGS, (LPBYTE)pbData, &cb, CRYPT_FIRST); Assert(f); do { // Walk through the list of all known S/MIME caps looking to see if we // have a match. for (i = 0; i < CEncAlgs; i++) { if ((RgAlgsDesc[i].dwFlags == (DWORD)(fEncryption ? flEncryption : flSigning)) && lstrcmpi(pbData->szName, RgAlgsDesc[i].szCSPAlgName) == 0) { if (pbData->dwBitLen > *pdwStrength) { *pdwStrength = pbData->dwBitLen; } } } cb = cbMax; f = CryptGetProvParam(hprov, PP_ENUMALGS, (LPBYTE)pbData, &cb, 0); } while (f); CryptReleaseContext(hprov, 0); SafeMemFree(pbData); // Try the enhanced provider? if (! pszProvName || (lstrcmpi(pszProvName, MS_DEF_PROV) == NULL)) { pszProvName = MS_ENHANCED_PROV; goto TryEnhanced; } } } exit: return(hr); } MIMEOLEAPI MimeOleSMimeCapsFull(LPVOID pv, BOOL fFullEncryption, BOOL fFullSigning, LPBYTE pbSymCaps, DWORD * pcbSymCaps) { CRYPT_SMIME_CAPABILITIES caps; BOOL f; DWORD i; DWORD j = 0; CRYPT_SMIME_CAPABILITY rgcaps[CEncAlgs]; LPBYTE rgfUse = (LPBYTE)pv; // // We need to build the list of encryption algs supported, if we have // a dialog box item, then use that to build the list. // if (fFullEncryption) { for (i = 0; i < CEncAlgs; i++) { if (rgfUse[i] && (RgAlgsDesc[i].dwFlags == flEncryption)) { rgcaps[j].pszObjId = RgAlgsDesc[i].pszObjId; rgcaps[j].Parameters.cbData = RgAlgsDesc[i].cbData; rgcaps[j].Parameters.pbData = (LPBYTE)RgAlgsDesc[i].pbData; j += 1; } } } else { // // Just assume that only 40-bit RC2 is supported // rgcaps[j].pszObjId = szOID_RSA_RC2CBC; rgcaps[j].Parameters.cbData = sizeof(RgbRc2_40bit); rgcaps[j].Parameters.pbData = (LPBYTE) RgbRc2_40bit; j += 1; } // // Now, put in the signing algorithms // if (fFullSigning) { for (i = 0; i < CEncAlgs; i++) { if (rgfUse[i] && (RgAlgsDesc[i].dwFlags == flSigning)) { rgcaps[j].pszObjId = RgAlgsDesc[i].pszObjId; rgcaps[j].Parameters.cbData = RgAlgsDesc[i].cbData; rgcaps[j].Parameters.pbData = (LPBYTE)RgAlgsDesc[i].pbData; j += 1; } } } else { // // Just assume that only SHA-1 is supported // rgcaps[j].pszObjId = szOID_OIWSEC_sha1RSASign; rgcaps[j].Parameters.cbData = 0; rgcaps[j].Parameters.pbData = NULL; j += 1; } // // Now actually encrypt the data and return the result. Note that we // don't allocate space but use the space allocated by our caller // caps.cCapability = j; caps.rgCapability = rgcaps; f = CryptEncodeObject(X509_ASN_ENCODING, PKCS_SMIME_CAPABILITIES, &caps, pbSymCaps, pcbSymCaps); return(f ? S_OK : E_FAIL); }