diff --git a/dlls/crypt32/main.c b/dlls/crypt32/main.c index aec288dbc23..9e53e4b5307 100644 --- a/dlls/crypt32/main.c +++ b/dlls/crypt32/main.c @@ -235,15 +235,3 @@ BOOL WINAPI CryptQueryObject(DWORD dwObjectType, const void* pvObject, phCertStore, phMsg, ppvContext); return FALSE; } - -BOOL WINAPI CryptVerifyMessageSignature(PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, - DWORD dwSignerIndex, const BYTE* pbSignedBlob, DWORD cbSignedBlob, - BYTE* pbDecoded, DWORD* pcbDecoded, PCCERT_CONTEXT* ppSignerCert) -{ - FIXME("stub: %p, %d, %p, %d, %p, %p, %p\n", - pVerifyPara, dwSignerIndex, pbSignedBlob, cbSignedBlob, - pbDecoded, pcbDecoded, ppSignerCert); - if (ppSignerCert) - *ppSignerCert = NULL; - return FALSE; -} diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index dea6a119b21..7cf1a301a2d 100644 --- a/dlls/crypt32/msg.c +++ b/dlls/crypt32/msg.c @@ -2321,3 +2321,135 @@ LONG WINAPI CryptGetMessageSignerCount(DWORD dwMsgEncodingType, } 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; + } + } + } + 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 CryptVerifyMessageSignature(PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, + DWORD dwSignerIndex, const BYTE* pbSignedBlob, DWORD cbSignedBlob, + BYTE* pbDecoded, DWORD* pcbDecoded, PCCERT_CONTEXT* ppSignerCert) +{ + BOOL ret = FALSE; + DWORD size; + CRYPT_CONTENT_INFO *contentInfo; + + TRACE("(%p, %d, %p, %d, %p, %p, %p)\n", + pVerifyPara, dwSignerIndex, pbSignedBlob, cbSignedBlob, + pbDecoded, pcbDecoded, ppSignerCert); + + if (ppSignerCert) + *ppSignerCert = NULL; + if (pcbDecoded) + *pcbDecoded = 0; + if (!pVerifyPara || + pVerifyPara->cbSize != sizeof(CRYPT_VERIFY_MESSAGE_PARA) || + GET_CMSG_ENCODING_TYPE(pVerifyPara->dwMsgAndCertEncodingType) != + PKCS_7_ASN_ENCODING) + { + SetLastError(E_INVALIDARG); + return FALSE; + } + + ret = CryptDecodeObjectEx(pVerifyPara->dwMsgAndCertEncodingType, + PKCS_CONTENT_INFO, pbSignedBlob, cbSignedBlob, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, + (LPBYTE)&contentInfo, &size); + if (ret) + { + if (strcmp(contentInfo->pszObjId, szOID_RSA_signedData)) + { + SetLastError(CRYPT_E_UNEXPECTED_MSG_TYPE); + ret = FALSE; + } + else + { + HCRYPTMSG msg = CryptMsgOpenToDecode( + pVerifyPara->dwMsgAndCertEncodingType, 0, CMSG_SIGNED, + pVerifyPara->hCryptProv, NULL, NULL); + + if (msg) + { + ret = CryptMsgUpdate(msg, contentInfo->Content.pbData, + contentInfo->Content.cbData, TRUE); + if (ret && pcbDecoded) + ret = CRYPT_CopyParam(pbDecoded, pcbDecoded, + contentInfo->Content.pbData, contentInfo->Content.cbData); + 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); + } + CryptMsgClose(msg); + } + } + LocalFree(contentInfo); + } + TRACE("returning %d\n", ret); + return ret; +} diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c index 008e60a7029..68487a51d81 100644 --- a/dlls/crypt32/tests/msg.c +++ b/dlls/crypt32/tests/msg.c @@ -2487,6 +2487,145 @@ static void test_msg_get_signer_count(void) ok(count == 1, "Expected 1, got %d\n", count); } +static const BYTE signedWithCertEmptyContent[] = { +0x30,0x81,0xdf,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x07,0x02,0xa0, +0x81,0xd1,0x30,0x81,0xce,0x02,0x01,0x01,0x31,0x0e,0x30,0x0c,0x06,0x08,0x2a, +0x86,0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x30,0x02,0x06,0x00,0xa0,0x7c, +0x30,0x7a,0x02,0x01,0x01,0x30,0x02,0x06,0x00,0x30,0x15,0x31,0x13,0x30,0x11, +0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e, +0x67,0x00,0x30,0x22,0x18,0x0f,0x31,0x36,0x30,0x31,0x30,0x31,0x30,0x31,0x30, +0x30,0x30,0x30,0x30,0x30,0x5a,0x18,0x0f,0x31,0x36,0x30,0x31,0x30,0x31,0x30, +0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x15,0x31,0x13,0x30,0x11,0x06, +0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67, +0x00,0x30,0x07,0x30,0x02,0x06,0x00,0x03,0x01,0x00,0xa3,0x16,0x30,0x14,0x30, +0x12,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x08,0x30,0x06,0x01,0x01, +0xff,0x02,0x01,0x01,0x31,0x37,0x30,0x35,0x02,0x01,0x01,0x30,0x1a,0x30,0x15, +0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e, +0x20,0x4c,0x61,0x6e,0x67,0x00,0x02,0x01,0x01,0x30,0x0c,0x06,0x08,0x2a,0x86, +0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x30,0x04,0x06,0x00,0x05,0x00,0x04, +0x00 }; +static const BYTE signedWithCertContent[] = { +0x30,0x82,0x01,0x32,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x07,0x02, +0xa0,0x82,0x01,0x23,0x30,0x82,0x01,0x1f,0x02,0x01,0x01,0x31,0x0e,0x30,0x0c, +0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x30,0x13,0x06, +0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x07,0x01,0xa0,0x06,0x04,0x04,0x01, +0x02,0x03,0x04,0xa0,0x7c,0x30,0x7a,0x02,0x01,0x01,0x30,0x02,0x06,0x00,0x30, +0x15,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61, +0x6e,0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x22,0x18,0x0f,0x31,0x36,0x30,0x31, +0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x18,0x0f,0x31,0x36, +0x30,0x31,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x15, +0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e, +0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x07,0x30,0x02,0x06,0x00,0x03,0x01,0x00, +0xa3,0x16,0x30,0x14,0x30,0x12,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04, +0x08,0x30,0x06,0x01,0x01,0xff,0x02,0x01,0x01,0x31,0x77,0x30,0x75,0x02,0x01, +0x01,0x30,0x1a,0x30,0x15,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13, +0x0a,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67,0x00,0x02,0x01,0x01,0x30, +0x0c,0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x30,0x04, +0x06,0x00,0x05,0x00,0x04,0x40,0x81,0xa6,0x70,0xb3,0xef,0x59,0xd1,0x66,0xd1, +0x9b,0xc0,0x9a,0xb6,0x9a,0x5e,0x6d,0x6f,0x6d,0x0d,0x59,0xa9,0xaa,0x6e,0xe9, +0x2c,0xa0,0x1e,0xee,0xc2,0x60,0xbc,0x59,0xbe,0x3f,0x63,0x06,0x8d,0xc9,0x11, +0x1d,0x23,0x64,0x92,0xef,0x2e,0xfc,0x57,0x29,0xa4,0xaf,0xe0,0xee,0x93,0x19, +0x39,0x51,0xe4,0x44,0xb8,0x0b,0x28,0xf4,0xa8,0x0d }; +static const BYTE signedWithCertWithPubKeyContent[] = { +0x30,0x81,0xfc,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x07,0x02,0xa0, +0x81,0xee,0x30,0x81,0xeb,0x02,0x01,0x01,0x31,0x0e,0x30,0x0c,0x06,0x08,0x2a, +0x86,0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x30,0x02,0x06,0x00,0xa0,0x81, +0x98,0x30,0x81,0x95,0x02,0x01,0x01,0x30,0x02,0x06,0x00,0x30,0x15,0x31,0x13, +0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,0x20,0x4c, +0x61,0x6e,0x67,0x00,0x30,0x22,0x18,0x0f,0x31,0x36,0x30,0x31,0x30,0x31,0x30, +0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x18,0x0f,0x31,0x36,0x30,0x31,0x30, +0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x15,0x31,0x13,0x30, +0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61, +0x6e,0x67,0x00,0x30,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d, +0x01,0x01,0x01,0x05,0x00,0x03,0x11,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06, +0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa3,0x16,0x30,0x14,0x30,0x12, +0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x08,0x30,0x06,0x01,0x01,0xff, +0x02,0x01,0x01,0x31,0x37,0x30,0x35,0x02,0x01,0x01,0x30,0x1a,0x30,0x15,0x31, +0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,0x20, +0x4c,0x61,0x6e,0x67,0x00,0x02,0x01,0x01,0x30,0x0c,0x06,0x08,0x2a,0x86,0x48, +0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x30,0x04,0x06,0x00,0x05,0x00,0x04,0x00 }; + +static void test_verify_message_signature(void) +{ + BOOL ret; + CRYPT_VERIFY_MESSAGE_PARA para = { 0 }; + PCCERT_CONTEXT cert; + DWORD cbDecoded; + + SetLastError(0xdeadbeef); + ret = CryptVerifyMessageSignature(NULL, 0, NULL, 0, NULL, 0, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08x\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = CryptVerifyMessageSignature(¶, 0, NULL, 0, NULL, 0, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08x\n", GetLastError()); + para.cbSize = sizeof(para); + SetLastError(0xdeadbeef); + ret = CryptVerifyMessageSignature(¶, 0, NULL, 0, NULL, 0, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08x\n", GetLastError()); + para.cbSize = 0; + para.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING; + SetLastError(0xdeadbeef); + ret = CryptVerifyMessageSignature(¶, 0, NULL, 0, NULL, 0, NULL); + ok(!ret && GetLastError() == E_INVALIDARG, + "Expected E_INVALIDARG, got %08x\n", GetLastError()); + para.cbSize = sizeof(para); + SetLastError(0xdeadbeef); + ret = CryptVerifyMessageSignature(¶, 0, NULL, 0, NULL, 0, NULL); + ok(!ret && GetLastError() == CRYPT_E_ASN1_EOD, + "Expected CRYPT_E_ASN1_EOD, got %08x\n", GetLastError()); + /* Check whether cert is set on error */ + cert = (PCCERT_CONTEXT)0xdeadbeef; + ret = CryptVerifyMessageSignature(¶, 0, NULL, 0, NULL, 0, &cert); + ok(cert == NULL, "Expected NULL cert\n"); + /* Check whether cbDecoded is set on error */ + cbDecoded = 0xdeadbeef; + ret = CryptVerifyMessageSignature(¶, 0, NULL, 0, NULL, &cbDecoded, + NULL); + ok(!cbDecoded, "Expected 0\n"); + SetLastError(0xdeadbeef); + ret = CryptVerifyMessageSignature(¶, 0, dataEmptyBareContent, + sizeof(dataEmptyBareContent), NULL, 0, NULL); + ok(!ret && GetLastError() == CRYPT_E_ASN1_BADTAG, + "Expected CRYPT_E_ASN1_BADTAG, got %08x\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = CryptVerifyMessageSignature(¶, 0, dataEmptyContent, + sizeof(dataEmptyContent), NULL, 0, NULL); + ok(!ret && GetLastError() == CRYPT_E_UNEXPECTED_MSG_TYPE, + "Expected CRYPT_E_UNEXPECTED_MSG_TYPE, got %08x\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = CryptVerifyMessageSignature(¶, 0, signedEmptyBareContent, + sizeof(signedEmptyBareContent), NULL, 0, NULL); + ok(!ret && GetLastError() == CRYPT_E_ASN1_BADTAG, + "Expected CRYPT_E_ASN1_BADTAG, got %08x\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = CryptVerifyMessageSignature(¶, 0, signedEmptyContent, + sizeof(signedEmptyContent), NULL, 0, NULL); + ok(!ret && GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND, got %08x\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = CryptVerifyMessageSignature(¶, 0, signedContent, + sizeof(signedContent), NULL, 0, NULL); + ok(!ret && GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND, got %08x\n", GetLastError()); + /* FIXME: Windows fails with CRYPT_E_NOT_FOUND for these messages, but + * their signer certs have invalid public keys that fail to decode. In + * Wine therefore the failure is an ASN error. Need some messages with + * valid public keys and invalid signatures to check against. + */ + ret = CryptVerifyMessageSignature(¶, 0, signedWithCertEmptyContent, + sizeof(signedWithCertEmptyContent), NULL, 0, NULL); + ok(!ret, "Expected failure\n"); + ret = CryptVerifyMessageSignature(¶, 0, signedWithCertContent, + sizeof(signedWithCertContent), NULL, 0, NULL); + ok(!ret, "Expected failure\n"); + ret = CryptVerifyMessageSignature(¶, 0, signedWithCertWithPubKeyContent, + sizeof(signedWithCertWithPubKeyContent), NULL, 0, NULL); + ok(!ret, "Expected failure\n"); +} + START_TEST(msg) { init_function_pointers(); @@ -2506,4 +2645,5 @@ START_TEST(msg) /* simplified message functions */ test_msg_get_signer_count(); + test_verify_message_signature(); }