/* * Copyright 2007 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include <stdarg.h> #include "windef.h" #include "winbase.h" #include "wincrypt.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(crypt); HCERTSTORE WINAPI CryptGetMessageCertificates(DWORD dwMsgAndCertEncodingType, HCRYPTPROV_LEGACY hCryptProv, DWORD dwFlags, const BYTE* pbSignedBlob, DWORD cbSignedBlob) { CRYPT_DATA_BLOB blob = { cbSignedBlob, (LPBYTE)pbSignedBlob }; TRACE("(%08x, %ld, %d08x %p, %d)\n", dwMsgAndCertEncodingType, hCryptProv, dwFlags, pbSignedBlob, cbSignedBlob); return CertOpenStore(CERT_STORE_PROV_PKCS7, dwMsgAndCertEncodingType, hCryptProv, dwFlags, &blob); } LONG WINAPI CryptGetMessageSignerCount(DWORD dwMsgEncodingType, const BYTE *pbSignedBlob, DWORD cbSignedBlob) { HCRYPTMSG msg; LONG count = -1; TRACE("(%08x, %p, %d)\n", dwMsgEncodingType, pbSignedBlob, cbSignedBlob); msg = CryptMsgOpenToDecode(dwMsgEncodingType, 0, 0, 0, NULL, NULL); if (msg) { if (CryptMsgUpdate(msg, pbSignedBlob, cbSignedBlob, TRUE)) { DWORD size = sizeof(count); CryptMsgGetParam(msg, CMSG_SIGNER_COUNT_PARAM, 0, &count, &size); } CryptMsgClose(msg); } return count; } static CERT_INFO *CRYPT_GetSignerCertInfoFromMsg(HCRYPTMSG msg, DWORD dwSignerIndex) { CERT_INFO *certInfo = NULL; DWORD size; if (CryptMsgGetParam(msg, CMSG_SIGNER_CERT_INFO_PARAM, dwSignerIndex, NULL, &size)) { certInfo = CryptMemAlloc(size); if (certInfo) { if (!CryptMsgGetParam(msg, CMSG_SIGNER_CERT_INFO_PARAM, dwSignerIndex, certInfo, &size)) { CryptMemFree(certInfo); certInfo = NULL; } } } else SetLastError(CRYPT_E_UNEXPECTED_MSG_TYPE); return certInfo; } static PCCERT_CONTEXT WINAPI CRYPT_DefaultGetSignerCertificate(void *pvGetArg, DWORD dwCertEncodingType, PCERT_INFO pSignerId, HCERTSTORE hMsgCertStore) { return CertFindCertificateInStore(hMsgCertStore, dwCertEncodingType, 0, CERT_FIND_SUBJECT_CERT, pSignerId, NULL); } static inline PCCERT_CONTEXT CRYPT_GetSignerCertificate(HCRYPTMSG msg, PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, PCERT_INFO certInfo, HCERTSTORE store) { PFN_CRYPT_GET_SIGNER_CERTIFICATE getCert; if (pVerifyPara->pfnGetSignerCertificate) getCert = pVerifyPara->pfnGetSignerCertificate; else getCert = CRYPT_DefaultGetSignerCertificate; return getCert(pVerifyPara->pvGetArg, pVerifyPara->dwMsgAndCertEncodingType, certInfo, store); } BOOL WINAPI CryptVerifyDetachedMessageSignature( PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, DWORD dwSignerIndex, const BYTE *pbDetachedSignBlob, DWORD cbDetachedSignBlob, DWORD cToBeSigned, const BYTE *rgpbToBeSigned[], DWORD rgcbToBeSigned[], PCCERT_CONTEXT *ppSignerCert) { BOOL ret = FALSE; HCRYPTMSG msg; TRACE("(%p, %d, %p, %d, %d, %p, %p, %p)\n", pVerifyPara, dwSignerIndex, pbDetachedSignBlob, cbDetachedSignBlob, cToBeSigned, rgpbToBeSigned, rgcbToBeSigned, ppSignerCert); if (ppSignerCert) *ppSignerCert = NULL; if (!pVerifyPara || pVerifyPara->cbSize != sizeof(CRYPT_VERIFY_MESSAGE_PARA) || GET_CMSG_ENCODING_TYPE(pVerifyPara->dwMsgAndCertEncodingType) != PKCS_7_ASN_ENCODING) { SetLastError(E_INVALIDARG); return FALSE; } msg = CryptMsgOpenToDecode(pVerifyPara->dwMsgAndCertEncodingType, CMSG_DETACHED_FLAG, 0, pVerifyPara->hCryptProv, NULL, NULL); if (msg) { ret = CryptMsgUpdate(msg, pbDetachedSignBlob, cbDetachedSignBlob, TRUE); if (ret) { DWORD i; for (i = 0; ret && i < cToBeSigned; i++) ret = CryptMsgUpdate(msg, rgpbToBeSigned[i], rgcbToBeSigned[i], i == cToBeSigned - 1); } if (ret) { CERT_INFO *certInfo = CRYPT_GetSignerCertInfoFromMsg(msg, dwSignerIndex); ret = FALSE; if (certInfo) { HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MSG, pVerifyPara->dwMsgAndCertEncodingType, pVerifyPara->hCryptProv, 0, msg); if (store) { PCCERT_CONTEXT cert = CRYPT_GetSignerCertificate( msg, pVerifyPara, certInfo, store); if (cert) { ret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_SIGNATURE, cert->pCertInfo); if (ret && ppSignerCert) *ppSignerCert = cert; else CertFreeCertificateContext(cert); } else SetLastError(CRYPT_E_NOT_FOUND); CertCloseStore(store, 0); } CryptMemFree(certInfo); } } CryptMsgClose(msg); } TRACE("returning %d\n", ret); return ret; } BOOL WINAPI CryptVerifyMessageSignature(PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, DWORD dwSignerIndex, const BYTE* pbSignedBlob, DWORD cbSignedBlob, BYTE* pbDecoded, DWORD* pcbDecoded, PCCERT_CONTEXT* ppSignerCert) { BOOL ret = FALSE; HCRYPTMSG msg; TRACE("(%p, %d, %p, %d, %p, %p, %p)\n", pVerifyPara, dwSignerIndex, pbSignedBlob, cbSignedBlob, pbDecoded, pcbDecoded, ppSignerCert); if (ppSignerCert) *ppSignerCert = NULL; if (!pVerifyPara || pVerifyPara->cbSize != sizeof(CRYPT_VERIFY_MESSAGE_PARA) || GET_CMSG_ENCODING_TYPE(pVerifyPara->dwMsgAndCertEncodingType) != PKCS_7_ASN_ENCODING) { if(pcbDecoded) *pcbDecoded = 0; SetLastError(E_INVALIDARG); return FALSE; } msg = CryptMsgOpenToDecode(pVerifyPara->dwMsgAndCertEncodingType, 0, 0, pVerifyPara->hCryptProv, NULL, NULL); if (msg) { ret = CryptMsgUpdate(msg, pbSignedBlob, cbSignedBlob, TRUE); if (ret) { CERT_INFO *certInfo = CRYPT_GetSignerCertInfoFromMsg(msg, dwSignerIndex); ret = FALSE; if (certInfo) { HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MSG, pVerifyPara->dwMsgAndCertEncodingType, pVerifyPara->hCryptProv, 0, msg); if (store) { PCCERT_CONTEXT cert = CRYPT_GetSignerCertificate( msg, pVerifyPara, certInfo, store); if (cert) { ret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_SIGNATURE, cert->pCertInfo); if (ret && ppSignerCert) *ppSignerCert = cert; else CertFreeCertificateContext(cert); } CertCloseStore(store, 0); } } CryptMemFree(certInfo); } if (ret) { /* The caller is expected to pass a valid pointer to pcbDecoded * when the message verifies successfully. */ if (pcbDecoded) ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, pbDecoded, pcbDecoded); else { SetLastError(CRYPT_E_NOT_FOUND); ret = FALSE; } } CryptMsgClose(msg); } if(!ret && pcbDecoded) *pcbDecoded = 0; TRACE("returning %d\n", ret); return ret; } BOOL WINAPI CryptHashMessage(PCRYPT_HASH_MESSAGE_PARA pHashPara, BOOL fDetachedHash, DWORD cToBeHashed, const BYTE *rgpbToBeHashed[], DWORD rgcbToBeHashed[], BYTE *pbHashedBlob, DWORD *pcbHashedBlob, BYTE *pbComputedHash, DWORD *pcbComputedHash) { DWORD i, flags; BOOL ret = FALSE; HCRYPTMSG msg; CMSG_HASHED_ENCODE_INFO info; TRACE("(%p, %d, %d, %p, %p, %p, %p, %p, %p)\n", pHashPara, fDetachedHash, cToBeHashed, rgpbToBeHashed, rgcbToBeHashed, pbHashedBlob, pcbHashedBlob, pbComputedHash, pcbComputedHash); if (pHashPara->cbSize != sizeof(CRYPT_HASH_MESSAGE_PARA)) { SetLastError(E_INVALIDARG); return FALSE; } /* Native seems to ignore any encoding type other than the expected * PKCS_7_ASN_ENCODING */ if (GET_CMSG_ENCODING_TYPE(pHashPara->dwMsgEncodingType) != PKCS_7_ASN_ENCODING) return TRUE; /* Native also seems to do nothing if the output parameter isn't given */ if (!pcbHashedBlob) return TRUE; flags = fDetachedHash ? CMSG_DETACHED_FLAG : 0; memset(&info, 0, sizeof(info)); info.cbSize = sizeof(info); info.hCryptProv = pHashPara->hCryptProv; memcpy(&info.HashAlgorithm, &pHashPara->HashAlgorithm, sizeof(info.HashAlgorithm)); info.pvHashAuxInfo = pHashPara->pvHashAuxInfo; msg = CryptMsgOpenToEncode(pHashPara->dwMsgEncodingType, flags, CMSG_HASHED, &info, NULL, NULL); if (msg) { for (i = 0, ret = TRUE; ret && i < cToBeHashed; i++) ret = CryptMsgUpdate(msg, rgpbToBeHashed[i], rgcbToBeHashed[i], i == cToBeHashed - 1); if (ret) { ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, pbHashedBlob, pcbHashedBlob); if (ret && pcbComputedHash) ret = CryptMsgGetParam(msg, CMSG_COMPUTED_HASH_PARAM, 0, pbComputedHash, pcbComputedHash); } CryptMsgClose(msg); } return ret; } BOOL WINAPI CryptVerifyDetachedMessageHash(PCRYPT_HASH_MESSAGE_PARA pHashPara, BYTE *pbDetachedHashBlob, DWORD cbDetachedHashBlob, DWORD cToBeHashed, const BYTE *rgpbToBeHashed[], DWORD rgcbToBeHashed[], BYTE *pbComputedHash, DWORD *pcbComputedHash) { HCRYPTMSG msg; BOOL ret = FALSE; TRACE("(%p, %p, %d, %d, %p, %p, %p, %p)\n", pHashPara, pbDetachedHashBlob, cbDetachedHashBlob, cToBeHashed, rgpbToBeHashed, rgcbToBeHashed, pbComputedHash, pcbComputedHash); if (pHashPara->cbSize != sizeof(CRYPT_HASH_MESSAGE_PARA)) { SetLastError(E_INVALIDARG); return FALSE; } if (GET_CMSG_ENCODING_TYPE(pHashPara->dwMsgEncodingType) != PKCS_7_ASN_ENCODING) { SetLastError(E_INVALIDARG); return FALSE; } msg = CryptMsgOpenToDecode(pHashPara->dwMsgEncodingType, CMSG_DETACHED_FLAG, 0, pHashPara->hCryptProv, NULL, NULL); if (msg) { DWORD i; ret = CryptMsgUpdate(msg, pbDetachedHashBlob, cbDetachedHashBlob, TRUE); if (ret) { if (cToBeHashed) { for (i = 0; ret && i < cToBeHashed; i++) { ret = CryptMsgUpdate(msg, rgpbToBeHashed[i], rgcbToBeHashed[i], i == cToBeHashed - 1); } } else ret = CryptMsgUpdate(msg, NULL, 0, TRUE); } if (ret) { ret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_HASH, NULL); if (ret && pcbComputedHash) ret = CryptMsgGetParam(msg, CMSG_COMPUTED_HASH_PARAM, 0, pbComputedHash, pcbComputedHash); } CryptMsgClose(msg); } return ret; } BOOL WINAPI CryptVerifyMessageHash(PCRYPT_HASH_MESSAGE_PARA pHashPara, BYTE *pbHashedBlob, DWORD cbHashedBlob, BYTE *pbToBeHashed, DWORD *pcbToBeHashed, BYTE *pbComputedHash, DWORD *pcbComputedHash) { HCRYPTMSG msg; BOOL ret = FALSE; TRACE("(%p, %p, %d, %p, %p, %p, %p)\n", pHashPara, pbHashedBlob, cbHashedBlob, pbToBeHashed, pcbToBeHashed, pbComputedHash, pcbComputedHash); if (pHashPara->cbSize != sizeof(CRYPT_HASH_MESSAGE_PARA)) { SetLastError(E_INVALIDARG); return FALSE; } if (GET_CMSG_ENCODING_TYPE(pHashPara->dwMsgEncodingType) != PKCS_7_ASN_ENCODING) { SetLastError(E_INVALIDARG); return FALSE; } msg = CryptMsgOpenToDecode(pHashPara->dwMsgEncodingType, 0, 0, pHashPara->hCryptProv, NULL, NULL); if (msg) { ret = CryptMsgUpdate(msg, pbHashedBlob, cbHashedBlob, TRUE); if (ret) { ret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_HASH, NULL); if (ret && pcbToBeHashed) ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, pbToBeHashed, pcbToBeHashed); if (ret && pcbComputedHash) ret = CryptMsgGetParam(msg, CMSG_COMPUTED_HASH_PARAM, 0, pbComputedHash, pcbComputedHash); } CryptMsgClose(msg); } return ret; } BOOL WINAPI CryptSignMessage(PCRYPT_SIGN_MESSAGE_PARA pSignPara, BOOL fDetachedSignature, DWORD cToBeSigned, const BYTE *rgpbToBeSigned[], DWORD rgcbToBeSigned[], BYTE *pbSignedBlob, DWORD *pcbSignedBlob) { HCRYPTPROV hCryptProv; BOOL ret, freeProv = FALSE; DWORD i, keySpec; PCERT_BLOB certBlob = NULL; PCRL_BLOB crlBlob = NULL; CMSG_SIGNED_ENCODE_INFO signInfo; CMSG_SIGNER_ENCODE_INFO signer; HCRYPTMSG msg = 0; TRACE("(%p, %d, %d, %p, %p, %p, %p)\n", pSignPara, fDetachedSignature, cToBeSigned, rgpbToBeSigned, rgcbToBeSigned, pbSignedBlob, pcbSignedBlob); if (pSignPara->cbSize != sizeof(CRYPT_SIGN_MESSAGE_PARA) || GET_CMSG_ENCODING_TYPE(pSignPara->dwMsgEncodingType) != PKCS_7_ASN_ENCODING) { *pcbSignedBlob = 0; SetLastError(E_INVALIDARG); return FALSE; } if (!pSignPara->pSigningCert) return TRUE; ret = CryptAcquireCertificatePrivateKey(pSignPara->pSigningCert, CRYPT_ACQUIRE_CACHE_FLAG, NULL, &hCryptProv, &keySpec, &freeProv); if (!ret) return FALSE; memset(&signer, 0, sizeof(signer)); signer.cbSize = sizeof(signer); signer.pCertInfo = pSignPara->pSigningCert->pCertInfo; signer.hCryptProv = hCryptProv; signer.dwKeySpec = keySpec; signer.HashAlgorithm = pSignPara->HashAlgorithm; signer.pvHashAuxInfo = pSignPara->pvHashAuxInfo; signer.cAuthAttr = pSignPara->cAuthAttr; signer.rgAuthAttr = pSignPara->rgAuthAttr; signer.cUnauthAttr = pSignPara->cUnauthAttr; signer.rgUnauthAttr = pSignPara->rgUnauthAttr; memset(&signInfo, 0, sizeof(signInfo)); signInfo.cbSize = sizeof(signInfo); signInfo.cSigners = 1; signInfo.rgSigners = &signer; if (pSignPara->cMsgCert) { certBlob = CryptMemAlloc(sizeof(CERT_BLOB) * pSignPara->cMsgCert); if (certBlob) { for (i = 0; i < pSignPara->cMsgCert; ++i) { certBlob[i].cbData = pSignPara->rgpMsgCert[i]->cbCertEncoded; certBlob[i].pbData = pSignPara->rgpMsgCert[i]->pbCertEncoded; } signInfo.cCertEncoded = pSignPara->cMsgCert; signInfo.rgCertEncoded = certBlob; } else ret = FALSE; } if (pSignPara->cMsgCrl) { crlBlob = CryptMemAlloc(sizeof(CRL_BLOB) * pSignPara->cMsgCrl); if (crlBlob) { for (i = 0; i < pSignPara->cMsgCrl; ++i) { crlBlob[i].cbData = pSignPara->rgpMsgCrl[i]->cbCrlEncoded; crlBlob[i].pbData = pSignPara->rgpMsgCrl[i]->pbCrlEncoded; } signInfo.cCrlEncoded = pSignPara->cMsgCrl; signInfo.rgCrlEncoded = crlBlob; } else ret = FALSE; } if (pSignPara->dwFlags || pSignPara->dwInnerContentType) FIXME("unimplemented feature\n"); if (ret) msg = CryptMsgOpenToEncode(pSignPara->dwMsgEncodingType, fDetachedSignature ? CMSG_DETACHED_FLAG : 0, CMSG_SIGNED, &signInfo, NULL, NULL); if (msg) { if (cToBeSigned) { for (i = 0; ret && i < cToBeSigned; ++i) { ret = CryptMsgUpdate(msg, rgpbToBeSigned[i], rgcbToBeSigned[i], i == cToBeSigned - 1); } } else ret = CryptMsgUpdate(msg, NULL, 0, TRUE); if (ret) ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, pbSignedBlob, pcbSignedBlob); CryptMsgClose(msg); } else ret = FALSE; CryptMemFree(crlBlob); CryptMemFree(certBlob); if (freeProv) CryptReleaseContext(hCryptProv, 0); return ret; } BOOL WINAPI CryptEncryptMessage(PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, DWORD cRecipientCert, PCCERT_CONTEXT rgpRecipientCert[], const BYTE *pbToBeEncrypted, DWORD cbToBeEncrypted, BYTE *pbEncryptedBlob, DWORD *pcbEncryptedBlob) { BOOL ret = TRUE; DWORD i; PCERT_INFO *certInfo = NULL; CMSG_ENVELOPED_ENCODE_INFO envelopedInfo; HCRYPTMSG msg = 0; TRACE("(%p, %d, %p, %p, %d, %p, %p)\n", pEncryptPara, cRecipientCert, rgpRecipientCert, pbToBeEncrypted, cbToBeEncrypted, pbEncryptedBlob, pcbEncryptedBlob); if (pEncryptPara->cbSize != sizeof(CRYPT_ENCRYPT_MESSAGE_PARA) || GET_CMSG_ENCODING_TYPE(pEncryptPara->dwMsgEncodingType) != PKCS_7_ASN_ENCODING) { *pcbEncryptedBlob = 0; SetLastError(E_INVALIDARG); return FALSE; } memset(&envelopedInfo, 0, sizeof(envelopedInfo)); envelopedInfo.cbSize = sizeof(envelopedInfo); envelopedInfo.hCryptProv = pEncryptPara->hCryptProv; envelopedInfo.ContentEncryptionAlgorithm = pEncryptPara->ContentEncryptionAlgorithm; envelopedInfo.pvEncryptionAuxInfo = pEncryptPara->pvEncryptionAuxInfo; if (cRecipientCert) { certInfo = CryptMemAlloc(sizeof(PCERT_INFO) * cRecipientCert); if (certInfo) { for (i = 0; i < cRecipientCert; ++i) certInfo[i] = rgpRecipientCert[i]->pCertInfo; envelopedInfo.cRecipients = cRecipientCert; envelopedInfo.rgpRecipientCert = certInfo; } else ret = FALSE; } if (ret) msg = CryptMsgOpenToEncode(pEncryptPara->dwMsgEncodingType, 0, CMSG_ENVELOPED, &envelopedInfo, NULL, NULL); if (msg) { ret = CryptMsgUpdate(msg, pbToBeEncrypted, cbToBeEncrypted, TRUE); if (ret) ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, pbEncryptedBlob, pcbEncryptedBlob); CryptMsgClose(msg); } else ret = FALSE; CryptMemFree(certInfo); if (!ret) *pcbEncryptedBlob = 0; return ret; }