You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1103 lines
36 KiB
1103 lines
36 KiB
#include "pch.hxx"
|
|
#include "demand.h"
|
|
#include <shlwapi.h>
|
|
#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; i2<CEncAlgs; i2++) {
|
|
if (RgAlgsDesc[i2].pszObjId == SzDES) {
|
|
rgfShow[i2] = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
err:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
MIMEOLEAPI MimeOleSMimeCapsToDlg(LPBYTE pbSymCaps, DWORD cbSymCaps, DWORD cCerts,
|
|
PCCERT_CONTEXT * rgCerts, HWND hwnd, DWORD idEncAlgs,
|
|
DWORD idSignAlgs, DWORD idBlob)
|
|
{
|
|
DWORD cb;
|
|
BOOL f;
|
|
HRESULT hr = E_FAIL;
|
|
DWORD i;
|
|
WPARAM j;
|
|
int iSignDef = -1;
|
|
int iEncDef = -1;
|
|
DWORD i2;
|
|
PCRYPT_SMIME_CAPABILITIES pcaps = NULL;
|
|
CHAR rgch[100];
|
|
BOOL rgfShow[CEncAlgs] = {0};
|
|
|
|
if (cbSymCaps != 0) {
|
|
|
|
if ((hr = HrDecodeObject(pbSymCaps, cbSymCaps, PKCS_SMIME_CAPABILITIES,
|
|
CRYPT_DECODE_NOCOPY_FLAG, &cb, (LPVOID *)&pcaps)) || ! pcaps) {
|
|
goto err;
|
|
}
|
|
|
|
Assert(pcaps);
|
|
|
|
//
|
|
// Filter the list of capabilities passed in by the list of items that
|
|
// we already know about. We don't display algorithms that we don't
|
|
// recognize.
|
|
//
|
|
|
|
for (i=0; i<pcaps->cCapability; i++) {
|
|
for (i2=0; i2<CEncAlgs; i2++) {
|
|
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)) {
|
|
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; i<CEncAlgs; i++) {
|
|
if (rgfShow[i] && (RgAlgsDesc[i].dwFlags == flEncryption)) {
|
|
j = SendDlgItemMessageA(hwnd, idEncAlgs, CB_ADDSTRING,
|
|
0, (LPARAM) RgAlgsDesc[i].szAlgName);
|
|
SendDlgItemMessageA(hwnd, idEncAlgs, CB_SETITEMDATA, j, i);
|
|
if (iEncDef == -1) {
|
|
iEncDef = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iEncDef != (DWORD)-1) {
|
|
SendDlgItemMessageA(hwnd, idEncAlgs, CB_SELECTSTRING,
|
|
(WPARAM) -1, (LPARAM) RgAlgsDesc[iEncDef].szAlgName);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now populate the Signature Alg combo box
|
|
//
|
|
|
|
if (idSignAlgs != 0) {
|
|
SendDlgItemMessageA(hwnd, idSignAlgs, CB_RESETCONTENT, 0, 0);
|
|
for (i=0; i<CEncAlgs; i++) {
|
|
if (rgfShow[i] && (RgAlgsDesc[i].dwFlags == flSigning)) {
|
|
j = SendDlgItemMessageA(hwnd, idSignAlgs, CB_ADDSTRING,
|
|
0, (LPARAM) RgAlgsDesc[i].szAlgName);
|
|
SendDlgItemMessageA(hwnd, idSignAlgs, CB_SETITEMDATA, j, i);
|
|
if (iSignDef == -1) {
|
|
iSignDef = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iSignDef != (DWORD)-1) {
|
|
SendDlgItemMessageA(hwnd, idSignAlgs, CB_SELECTSTRING,
|
|
(WPARAM) -1, (LPARAM) RgAlgsDesc[iSignDef].szAlgName);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finally, lets play with the question of perference for signed blob data
|
|
//
|
|
|
|
if (idBlob != 0) {
|
|
SendDlgItemMessageA(hwnd, idBlob, BM_SETCHECK, rgfShow[CEncAlgs-1], 0);
|
|
}
|
|
|
|
hr = S_OK;
|
|
err:
|
|
SafeMemFree(pcaps);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
MIMEOLEAPI MimeOleSMimeCapsFromDlg(HWND hwnd, DWORD idEncAlgs, DWORD idSignAlgs,
|
|
DWORD idBlob, LPBYTE pbSymCaps, DWORD * pcbSymCaps)
|
|
{
|
|
DWORD c;
|
|
CRYPT_SMIME_CAPABILITIES caps;
|
|
BOOL f;
|
|
int fBlob = FALSE;
|
|
DWORD i;
|
|
DWORD i1;
|
|
DWORD j;
|
|
DWORD iEncDef = (DWORD) -1;
|
|
DWORD iSignDef = (DWORD) -1;
|
|
CRYPT_SMIME_CAPABILITY rgcaps[CEncAlgs];
|
|
BOOL rgfShow[CEncAlgs] = {0};
|
|
|
|
//
|
|
// If we were passed a combo box for the encryption alg, then we pull
|
|
// the default information out of it.
|
|
//
|
|
// Additionally we are going to pull out information about which algs
|
|
// are currently supported by the CSPs involved in the process. This
|
|
// will have been populated from a previous call to SymCapsToDlg
|
|
//
|
|
|
|
if (idEncAlgs != 0) {
|
|
i = (DWORD) SendDlgItemMessageA(hwnd, idEncAlgs, CB_GETCURSEL, 0, 0);
|
|
iEncDef = (DWORD) SendDlgItemMessageA(hwnd, idEncAlgs, CB_GETITEMDATA, i, 0);
|
|
|
|
c = (DWORD) SendDlgItemMessageA(hwnd, idEncAlgs, CB_GETCOUNT, 0, 0);
|
|
for (i=0; i<c; i++) {
|
|
i1 = (DWORD) SendDlgItemMessageA(hwnd, idEncAlgs, CB_GETITEMDATA, i, 0);
|
|
if (i1 < CEncAlgs) {
|
|
rgfShow[i1] = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we were passed a combo box for the signing algs, then we pull the
|
|
// default information out of it.
|
|
//
|
|
// Additionally, we are going to pull out information about which algs
|
|
// are currently supported by the CSPs involved in the in process. This
|
|
// will have been populated from a previous call to SymCapsToDlg.
|
|
//
|
|
|
|
if (idSignAlgs != 0) {
|
|
i = (DWORD) SendDlgItemMessageA(hwnd, idSignAlgs, CB_GETCURSEL, 0, 0);
|
|
iSignDef = (DWORD) SendDlgItemMessageA(hwnd, idSignAlgs, CB_GETITEMDATA, i, 0);
|
|
|
|
c = (DWORD) SendDlgItemMessageA(hwnd, idSignAlgs, CB_GETCOUNT, 0, 0);
|
|
for (i=0; i<c; i++) {
|
|
i1 = (DWORD) SendDlgItemMessageA(hwnd, idSignAlgs, CB_GETITEMDATA, i, 0);
|
|
if (i1 < CEncAlgs) {
|
|
rgfShow[i1] = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
j = 0;
|
|
if (idEncAlgs != 0) {
|
|
//
|
|
// If we have a default encryuption alg, then put it first
|
|
//
|
|
|
|
if (iEncDef != -1) {
|
|
rgcaps[j].pszObjId = RgAlgsDesc[iEncDef].pszObjId;
|
|
rgcaps[j].Parameters.cbData = RgAlgsDesc[iEncDef].cbData;
|
|
rgcaps[j].Parameters.pbData = (LPBYTE) RgAlgsDesc[iEncDef].pbData;
|
|
j += 1;
|
|
}
|
|
|
|
//
|
|
// We need to build the list of encryption algs supported, if we have
|
|
// a dialog box item, then use that to build the list.
|
|
//
|
|
|
|
for (i=0; i<CEncAlgs; i++) {
|
|
if (rgfShow[i] && (RgAlgsDesc[i].dwFlags == flEncryption) && (iEncDef != i)) {
|
|
rgcaps[j].pszObjId = RgAlgsDesc[i].pszObjId;
|
|
rgcaps[j].Parameters.cbData = RgAlgsDesc[i].cbData;
|
|
rgcaps[j].Parameters.pbData = (LPBYTE) RgAlgsDesc[i].pbData;
|
|
j += 1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// No dialog, so we are just going to 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;
|
|
}
|
|
|
|
if (idSignAlgs != 0) {
|
|
if (iSignDef != -1) {
|
|
rgcaps[j].pszObjId = RgAlgsDesc[iSignDef].pszObjId;
|
|
rgcaps[j].Parameters.cbData = RgAlgsDesc[iSignDef].cbData;
|
|
rgcaps[j].Parameters.pbData = (LPBYTE) RgAlgsDesc[iSignDef].pbData;
|
|
j += 1;
|
|
}
|
|
|
|
for (i=0; i<CEncAlgs; i++) {
|
|
if (rgfShow[i] && (RgAlgsDesc[i].dwFlags == flSigning) && (iSignDef != i)) {
|
|
rgcaps[j].pszObjId = RgAlgsDesc[i].pszObjId;
|
|
rgcaps[j].Parameters.cbData = RgAlgsDesc[i].cbData;
|
|
rgcaps[j].Parameters.pbData = (LPBYTE) RgAlgsDesc[i].pbData;
|
|
j += 1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// No dialog, so we are just going to 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;
|
|
}
|
|
|
|
//
|
|
// If we were passed in an ID blob item, then we should see if we are
|
|
// going to force a send in blob format
|
|
//
|
|
|
|
if (idBlob != 0) {
|
|
if (SendDlgItemMessageA(hwnd, idBlob, BM_GETCHECK, 0, 0) == 1) {
|
|
rgcaps[j].pszObjId = RgAlgsDesc[CEncAlgs-1].pszObjId;
|
|
rgcaps[j].Parameters.cbData = RgAlgsDesc[CEncAlgs-1].cbData;
|
|
rgcaps[j].Parameters.pbData = (LPBYTE) RgAlgsDesc[CEncAlgs-1].pbData;
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
static HRESULT SymCapAdd(LPBYTE pbSymCaps, DWORD cbSymCaps, BYTE * rgbFilter)
|
|
{
|
|
DWORD cb;
|
|
BOOL f;
|
|
HRESULT hr;
|
|
DWORD i;
|
|
DWORD i2;
|
|
PCRYPT_SMIME_CAPABILITIES pcaps = NULL;
|
|
|
|
//
|
|
// Take the sym caps and decode it
|
|
//
|
|
|
|
if ((hr = HrDecodeObject(pbSymCaps, cbSymCaps, PKCS_SMIME_CAPABILITIES,
|
|
CRYPT_DECODE_NOCOPY_FLAG, &cb, (LPVOID *)&pcaps)) || ! pcaps) {
|
|
goto err;
|
|
}
|
|
|
|
Assert(pcaps);
|
|
|
|
//
|
|
// Filter the list of capabilities passed in by the list of items that
|
|
// are on the list.
|
|
//
|
|
|
|
for (i2=0, f = TRUE; i2<CEncAlgs; i2++) {
|
|
if (rgbFilter[i2] == FALSE) {
|
|
f = FALSE;
|
|
continue;
|
|
}
|
|
|
|
for (i=0; i<pcaps->cCapability; 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; i<CEncAlgs; i++) pb[i] = TRUE;
|
|
|
|
hr = SymCapAdd(pbSymCapSender, cbSymCapSender, pb);
|
|
if (FAILED(hr)) {
|
|
MemFree(pb);
|
|
goto exit;
|
|
}
|
|
// Assert(hr == S_OK);
|
|
} else {
|
|
HCRYPTPROV hprov = NULL;
|
|
LPTSTR pszProvName = NULL; // use default provider
|
|
DWORD dwProvType = PROV_RSA_FULL;
|
|
BOOL f;
|
|
ULONG cb;
|
|
|
|
// No sender symcap specified. Init it to the highest available.
|
|
for (i = 0; i < CEncAlgs; i++) { // init to all false
|
|
pb[i] = FALSE;
|
|
}
|
|
|
|
TryEnhanced:
|
|
// Open the provider
|
|
hr = E_OUTOFMEMORY;
|
|
f = CryptAcquireContext(&hprov, NULL, pszProvName, dwProvType, CRYPT_VERIFYCONTEXT);
|
|
if (f) {
|
|
DWORD cbMax;
|
|
PROV_ENUMALGS * pbData = NULL;
|
|
|
|
hr = S_OK;
|
|
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++) {
|
|
// Assume if we get the correct algorithm name, that the CAPI
|
|
// bitLen parameter is a max value and we will support all smaller ones
|
|
// as well.
|
|
if (lstrcmpi(pbData->szName, 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; i<CEncAlgs; i++) {
|
|
if ((i != iSkip) && (RgAlgsDesc[i].dwFlags == flEncryption)) {
|
|
pb[i] &= 0;
|
|
f = pb[i];
|
|
}
|
|
}
|
|
|
|
return f ? S_OK : S_FALSE;
|
|
}
|
|
|
|
HRESULT GetResult(DWORD iTarget, LPBYTE pb, LPBYTE pbEncode, DWORD * pcbEncode,
|
|
DWORD * pdw)
|
|
{
|
|
CRYPT_SMIME_CAPABILITY cap;
|
|
CRYPT_SMIME_CAPABILITIES caps;
|
|
BOOL f = FALSE;
|
|
int i;
|
|
|
|
//
|
|
// Look for the first possible answer to the question
|
|
//
|
|
|
|
for (i=0; i<CEncAlgs; i++) {
|
|
if (RgAlgsDesc[i].dwFlags == iTarget) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Assert(i != CEncAlgs);
|
|
|
|
//
|
|
// Look for the highest possible alg to send data with
|
|
//
|
|
|
|
for (; i<CEncAlgs; i++) {
|
|
if ((RgAlgsDesc[i].dwFlags != iTarget) || pb[i]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We must not have run off the end of the array, all hash algs come after encryption
|
|
// algs
|
|
//
|
|
|
|
Assert( i < CEncAlgs );
|
|
|
|
//
|
|
// If did not find an algorithm, return the appropriate error
|
|
//
|
|
|
|
if (RgAlgsDesc[i].dwFlags != iTarget) {
|
|
*pcbEncode = 0;
|
|
if (pdw != NULL) {
|
|
*pdw = 0;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// Build the S/MIME Capability string with just this one item in it
|
|
//
|
|
|
|
caps.cCapability = 1;
|
|
caps.rgCapability = ∩
|
|
|
|
cap.pszObjId = RgAlgsDesc[i].pszObjId;
|
|
cap.Parameters.cbData = RgAlgsDesc[i].cbData;
|
|
cap.Parameters.pbData = (LPBYTE)RgAlgsDesc[i].pbData;
|
|
|
|
//
|
|
// Determine the "extra" parameter. For encryption it is the
|
|
// bit size of the algorithm. For Signing it is weither we should be doing
|
|
// blob signed
|
|
//
|
|
|
|
if (pdw != NULL) {
|
|
if (iTarget == 1) {
|
|
*pdw = RgAlgsDesc[i].dwBits;
|
|
}
|
|
else {
|
|
Assert(iTarget == 2);
|
|
*pdw = pb[CEncAlgs-1];
|
|
}
|
|
}
|
|
|
|
f = CryptEncodeObject(X509_ASN_ENCODING, PKCS_SMIME_CAPABILITIES,
|
|
&caps, pbEncode, pcbEncode);
|
|
#ifndef WIN16
|
|
if (!f && (::GetLastError() != ERROR_MORE_DATA)) {
|
|
return E_FAIL;
|
|
}
|
|
#endif
|
|
return f ? S_OK : S_FALSE;
|
|
}
|
|
|
|
MIMEOLEAPI MimeOleSMimeCapGetEncAlg(LPVOID pv, LPBYTE pbEncode, DWORD * pcbEncode,
|
|
DWORD * pdwBits)
|
|
{
|
|
return GetResult(1, (LPBYTE) pv, pbEncode, pcbEncode, pdwBits);
|
|
}
|
|
|
|
MIMEOLEAPI MimeOleSMimeCapGetHashAlg(LPVOID pv, LPBYTE pbEncode, DWORD * pcbEncode,
|
|
DWORD * pfBlobSign)
|
|
{
|
|
return GetResult(2, (LPBYTE) pv, pbEncode, pcbEncode, pfBlobSign);
|
|
}
|
|
|
|
MIMEOLEAPI MimeOleSMimeCapRelease(LPVOID pv)
|
|
{
|
|
MemFree(pv);
|
|
return S_OK;
|
|
}
|
|
|
|
MIMEOLEAPI MimeOleAlgNameFromSMimeCap(LPBYTE pbEncode, DWORD cbEncode,
|
|
LPCSTR * ppszProtocol)
|
|
{
|
|
DWORD cb = 0;
|
|
BOOL f;
|
|
HRESULT hr;
|
|
DWORD i;
|
|
PCRYPT_SMIME_CAPABILITIES pcaps = NULL;
|
|
|
|
//
|
|
// Decode the S/MIME caps which is passed in, allocate space to hold
|
|
// the resulting value
|
|
//
|
|
|
|
hr = HrDecodeObject(pbEncode, cbEncode, PKCS_SMIME_CAPABILITIES, CRYPT_DECODE_NOCOPY_FLAG, &cb, (LPVOID *)&pcaps);
|
|
if (FAILED(hr) || NULL == pcaps)
|
|
{
|
|
if (hr != E_OUTOFMEMORY)
|
|
{
|
|
if (RgchUnknown[0] == 0)
|
|
{
|
|
LoadStringA(g_hLocRes, IDS_UNKNOWN_ALG, RgchUnknown, sizeof(RgchUnknown));
|
|
}
|
|
*ppszProtocol = RgchUnknown;
|
|
return S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
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; i<CEncAlgs; i++) {
|
|
if ((strcmp(pcaps->rgCapability[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; i<CEncAlgs; i++) {
|
|
if ((strcmp(pcaps->rgCapability[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);
|
|
}
|