diff options
author | Ashod Nakashian <ashod.nakashian@collabora.co.uk> | 2017-07-09 09:42:01 -0400 |
---|---|---|
committer | Ashod Nakashian <ashnakash@gmail.com> | 2017-07-17 00:12:10 +0200 |
commit | 4f17445c12dc26c4881c4e486215b58d26515f8d (patch) | |
tree | d5532f4a3129fe532b4ad4305a44a8817c7e8b41 /svl | |
parent | c76c3655a394462b7b23bdfe6da4542fbdf30fbb (diff) |
svl: move byte-array verification from vcl
Also use comphelper::Base64 and
DateTime::CreateFromUnixTime to avoid depending on sax.
Change-Id: If1853f8d9481c9caa0625a111707531bbc495f75
Reviewed-on: https://gerrit.libreoffice.org/39993
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
Diffstat (limited to 'svl')
-rw-r--r-- | svl/Library_svl.mk | 19 | ||||
-rw-r--r-- | svl/source/crypto/cryptosign.cxx | 637 |
2 files changed, 654 insertions, 2 deletions
diff --git a/svl/Library_svl.mk b/svl/Library_svl.mk index e3969b1d90a3..97fa4df3add9 100644 --- a/svl/Library_svl.mk +++ b/svl/Library_svl.mk @@ -95,6 +95,25 @@ $(eval $(call gb_Library_use_system_win32_libs,svl,\ shlwapi \ )) +ifeq ($(OS),WNT) +$(eval $(call gb_Library_add_defs,svl,\ + -DSVL_CRYPTO_MSCRYPTO \ +)) +$(eval $(call gb_Library_use_system_win32_libs,svl,\ + crypt32 \ +)) +else +ifneq (,$(filter DESKTOP,$(BUILD_TYPE))) +$(eval $(call gb_Library_add_defs,svl,\ + -DSVL_CRYPTO_NSS \ +)) +$(eval $(call gb_Library_use_externals,svl,\ + nss3 \ + plc4 \ +)) +endif # BUILD_TYPE=DESKTOP +endif + $(eval $(call gb_Library_add_exception_objects,svl,\ svl/source/config/asiancfg \ svl/source/config/cjkoptions \ diff --git a/svl/source/crypto/cryptosign.cxx b/svl/source/crypto/cryptosign.cxx index 0c16ba6fbd94..d5304e5e2d2a 100644 --- a/svl/source/crypto/cryptosign.cxx +++ b/svl/source/crypto/cryptosign.cxx @@ -12,12 +12,12 @@ #include <rtl/character.hxx> #include <rtl/strbuf.hxx> #include <rtl/string.hxx> +#include <tools/datetime.hxx> #include <tools/stream.hxx> +#include <comphelper/base64.hxx> #include <comphelper/random.hxx> #include <comphelper/hash.hxx> #include <comphelper/processfactory.hxx> -// #include <comphelper/scopeguard.hxx> -// #include <comphelper/string.hxx> #include <com/sun/star/security/XCertificate.hpp> #include <com/sun/star/uno/Sequence.hxx> #include <filter/msfilter/mscodec.hxx> @@ -1646,6 +1646,639 @@ bool Signing::Sign(OStringBuffer& rCMSHexBuffer) #endif } +namespace +{ +#ifdef SVL_CRYPTO_NSS +/// Similar to NSS_CMSAttributeArray_FindAttrByOidTag(), but works directly with a SECOidData. +NSSCMSAttribute* CMSAttributeArray_FindAttrByOidData(NSSCMSAttribute** attrs, SECOidData* oid, PRBool only) +{ + NSSCMSAttribute* attr1, *attr2; + + if (attrs == nullptr) + return nullptr; + + if (oid == nullptr) + return nullptr; + + while ((attr1 = *attrs++) != nullptr) + { + if (attr1->type.len == oid->oid.len && PORT_Memcmp(attr1->type.data, + oid->oid.data, + oid->oid.len) == 0) + break; + } + + if (attr1 == nullptr) + return nullptr; + + if (!only) + return attr1; + + while ((attr2 = *attrs++) != nullptr) + { + if (attr2->type.len == oid->oid.len && PORT_Memcmp(attr2->type.data, + oid->oid.data, + oid->oid.len) == 0) + break; + } + + if (attr2 != nullptr) + return nullptr; + + return attr1; +} + +/// Same as SEC_StringToOID(), which is private to us. +SECStatus StringToOID(SECItem* to, const char* from, PRUint32 len) +{ + PRUint32 decimal_numbers = 0; + PRUint32 result_bytes = 0; + SECStatus rv; + PRUint8 result[1024]; + + static const PRUint32 max_decimal = (0xffffffff / 10); + static const char OIDstring[] = {"OID."}; + + if (!from || !to) + { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (!len) + { + len = PL_strlen(from); + } + if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4)) + { + from += 4; /* skip leading "OID." if present */ + len -= 4; + } + if (!len) + { +bad_data: + PORT_SetError(SEC_ERROR_BAD_DATA); + return SECFailure; + } + do + { + PRUint32 decimal = 0; + while (len > 0 && rtl::isAsciiDigit(static_cast<unsigned char>(*from))) + { + PRUint32 addend = (*from++ - '0'); + --len; + if (decimal > max_decimal) /* overflow */ + goto bad_data; + decimal = (decimal * 10) + addend; + if (decimal < addend) /* overflow */ + goto bad_data; + } + if (len != 0 && *from != '.') + { + goto bad_data; + } + if (decimal_numbers == 0) + { + if (decimal > 2) + goto bad_data; + result[0] = decimal * 40; + result_bytes = 1; + } + else if (decimal_numbers == 1) + { + if (decimal > 40) + goto bad_data; + result[0] += decimal; + } + else + { + /* encode the decimal number, */ + PRUint8* rp; + PRUint32 num_bytes = 0; + PRUint32 tmp = decimal; + while (tmp) + { + num_bytes++; + tmp >>= 7; + } + if (!num_bytes) + ++num_bytes; /* use one byte for a zero value */ + if (static_cast<size_t>(num_bytes) + result_bytes > sizeof result) + goto bad_data; + tmp = num_bytes; + rp = result + result_bytes - 1; + rp[tmp] = (PRUint8)(decimal & 0x7f); + decimal >>= 7; + while (--tmp > 0) + { + rp[tmp] = (PRUint8)(decimal | 0x80); + decimal >>= 7; + } + result_bytes += num_bytes; + } + ++decimal_numbers; + if (len > 0) /* skip trailing '.' */ + { + ++from; + --len; + } + } + while (len > 0); + /* now result contains result_bytes of data */ + if (to->data && to->len >= result_bytes) + { + PORT_Memcpy(to->data, result, to->len = result_bytes); + rv = SECSuccess; + } + else + { + SECItem result_item = {siBuffer, nullptr, 0 }; + result_item.data = result; + result_item.len = result_bytes; + rv = SECITEM_CopyItem(nullptr, to, &result_item); + } + return rv; +} +#elif defined SVL_CRYPTO_MSCRYPTO +/// Verifies a non-detached signature using CryptoAPI. +bool VerifyNonDetachedSignature(SvStream& rStream, const std::vector<std::pair<size_t, size_t>>& rByteRanges, std::vector<BYTE>& rExpectedHash) +{ + HCRYPTPROV hProv = 0; + if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) + { + SAL_WARN("xmlsecurity.pdfio", "CryptAcquireContext() failed"); + return false; + } + + HCRYPTHASH hHash = 0; + if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) + { + SAL_WARN("xmlsecurity.pdfio", "CryptCreateHash() failed"); + return false; + } + + for (const auto& rByteRange : rByteRanges) + { + rStream.Seek(rByteRange.first); + const int nChunkLen = 4096; + std::vector<unsigned char> aBuffer(nChunkLen); + for (size_t nByte = 0; nByte < rByteRange.second;) + { + size_t nRemainingSize = rByteRange.second - nByte; + if (nRemainingSize < nChunkLen) + { + rStream.ReadBytes(aBuffer.data(), nRemainingSize); + if (!CryptHashData(hHash, aBuffer.data(), nRemainingSize, 0)) + { + SAL_WARN("xmlsecurity.pdfio", "CryptHashData() failed"); + return false; + } + nByte = rByteRange.second; + } + else + { + rStream.ReadBytes(aBuffer.data(), nChunkLen); + if (!CryptHashData(hHash, aBuffer.data(), nChunkLen, 0)) + { + SAL_WARN("xmlsecurity.pdfio", "CryptHashData() failed"); + return false; + } + nByte += nChunkLen; + } + } + } + + DWORD nActualHash = 0; + if (!CryptGetHashParam(hHash, HP_HASHVAL, nullptr, &nActualHash, 0)) + { + SAL_WARN("xmlsecurity.pdfio", "CryptGetHashParam() failed to provide the hash length"); + return false; + } + + std::vector<unsigned char> aActualHash(nActualHash); + if (!CryptGetHashParam(hHash, HP_HASHVAL, aActualHash.data(), &nActualHash, 0)) + { + SAL_WARN("xmlsecurity.pdfio", "CryptGetHashParam() failed to provide the hash"); + return false; + } + + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + + if (!std::memcmp(aActualHash.data(), rExpectedHash.data(), aActualHash.size()) && aActualHash.size() == rExpectedHash.size()) + return true; + + return false; +} +#endif +} + +bool Signing::Verify(SvStream& rStream, + const std::vector<std::pair<size_t, size_t>>& aByteRanges, + const bool bNonDetached, + const std::vector<unsigned char>& aSignature, + SignatureInformation& rInformation) +{ + +#ifdef SVL_CRYPTO_NSS + // Validate the signature. No need to call NSS_Init() here, assume that the + // caller did that already. + + SECItem aSignatureItem; + aSignatureItem.data = const_cast<unsigned char*>(aSignature.data()); + aSignatureItem.len = aSignature.size(); + NSSCMSMessage* pCMSMessage = NSS_CMSMessage_CreateFromDER(&aSignatureItem, + /*cb=*/nullptr, + /*cb_arg=*/nullptr, + /*pwfn=*/nullptr, + /*pwfn_arg=*/nullptr, + /*decrypt_key_cb=*/nullptr, + /*decrypt_key_cb_arg=*/nullptr); + if (!NSS_CMSMessage_IsSigned(pCMSMessage)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: message is not signed"); + return false; + } + + NSSCMSContentInfo* pCMSContentInfo = NSS_CMSMessage_ContentLevel(pCMSMessage, 0); + if (!pCMSContentInfo) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSMessage_ContentLevel() failed"); + return false; + } + + auto pCMSSignedData = static_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(pCMSContentInfo)); + if (!pCMSSignedData) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSContentInfo_GetContent() failed"); + return false; + } + + // Import certificates from the signed data temporarily, so it'll be + // possible to verify the signature, even if we didn't have the certificate + // previously. + std::vector<CERTCertificate*> aDocumentCertificates; + for (size_t i = 0; pCMSSignedData->rawCerts[i]; ++i) + aDocumentCertificates.push_back(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), pCMSSignedData->rawCerts[i], nullptr, 0, 0)); + + NSSCMSSignerInfo* pCMSSignerInfo = NSS_CMSSignedData_GetSignerInfo(pCMSSignedData, 0); + if (!pCMSSignerInfo) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSSignedData_GetSignerInfo() failed"); + return false; + } + + SECItem aAlgorithm = NSS_CMSSignedData_GetDigestAlgs(pCMSSignedData)[0]->algorithm; + SECOidTag eOidTag = SECOID_FindOIDTag(&aAlgorithm); + + // Map a sign algorithm to a digest algorithm. + // See NSS_CMSUtil_MapSignAlgs(), which is private to us. + switch (eOidTag) + { + case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: + eOidTag = SEC_OID_SHA1; + break; + case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: + eOidTag = SEC_OID_SHA256; + break; + case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: + eOidTag = SEC_OID_SHA512; + break; + default: + break; + } + + HASH_HashType eHashType = HASH_GetHashTypeByOidTag(eOidTag); + HASHContext* pHASHContext = HASH_Create(eHashType); + if (!pHASHContext) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: HASH_Create() failed"); + return false; + } + + // We have a hash, update it with the byte ranges. + for (const auto& rByteRange : aByteRanges) + { + rStream.Seek(rByteRange.first); + + // And now hash this byte range. + const int nChunkLen = 4096; + std::vector<unsigned char> aBuffer(nChunkLen); + for (size_t nByte = 0; nByte < rByteRange.second;) + { + size_t nRemainingSize = rByteRange.second - nByte; + if (nRemainingSize < nChunkLen) + { + rStream.ReadBytes(aBuffer.data(), nRemainingSize); + HASH_Update(pHASHContext, aBuffer.data(), nRemainingSize); + nByte = rByteRange.second; + } + else + { + rStream.ReadBytes(aBuffer.data(), nChunkLen); + HASH_Update(pHASHContext, aBuffer.data(), nChunkLen); + nByte += nChunkLen; + } + } + } + + // Find out what is the expected length of the hash. + unsigned int nMaxResultLen = 0; + switch (eOidTag) + { + case SEC_OID_SHA1: + nMaxResultLen = msfilter::SHA1_HASH_LENGTH; + rInformation.nDigestID = xml::crypto::DigestID::SHA1; + break; + case SEC_OID_SHA256: + nMaxResultLen = msfilter::SHA256_HASH_LENGTH; + rInformation.nDigestID = xml::crypto::DigestID::SHA256; + break; + case SEC_OID_SHA512: + nMaxResultLen = msfilter::SHA512_HASH_LENGTH; + break; + default: + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: unrecognized algorithm"); + return false; + } + + auto pActualResultBuffer = static_cast<unsigned char*>(PORT_Alloc(nMaxResultLen)); + unsigned int nActualResultLen; + HASH_End(pHASHContext, pActualResultBuffer, &nActualResultLen, nMaxResultLen); + + CERTCertificate* pCertificate = NSS_CMSSignerInfo_GetSigningCertificate(pCMSSignerInfo, CERT_GetDefaultCertDB()); + if (!pCertificate) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSSignerInfo_GetSigningCertificate() failed"); + return false; + } + else + { + uno::Sequence<sal_Int8> aDerCert(pCertificate->derCert.len); + for (size_t i = 0; i < pCertificate->derCert.len; ++i) + aDerCert[i] = pCertificate->derCert.data[i]; + OUStringBuffer aBuffer; + comphelper::Base64::encode(aBuffer, aDerCert); + rInformation.ouX509Certificate = aBuffer.makeStringAndClear(); + } + + PRTime nSigningTime; + // This may fail, in which case the date should be taken from the dictionary's "M" key. + if (NSS_CMSSignerInfo_GetSigningTime(pCMSSignerInfo, &nSigningTime) == SECSuccess) + { + // First convert the UTC UNIX timestamp to a tools::DateTime. + // nSigningTime is in microseconds. + DateTime aDateTime = DateTime::CreateFromUnixTime(static_cast<double>(nSigningTime) / 1000000); + + // Then convert to a local UNO DateTime. + aDateTime.ConvertToLocalTime(); + rInformation.stDateTime = aDateTime.GetUNODateTime(); + } + + // Check if we have a signing certificate attribute. + SECOidData aOidData; + aOidData.oid.data = nullptr; + /* + * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::= + * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) + * smime(16) id-aa(2) 47 } + */ + if (StringToOID(&aOidData.oid, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess) + { + SAL_WARN("xmlsecurity.pdfio", "StringToOID() failed"); + return false; + } + aOidData.offset = SEC_OID_UNKNOWN; + aOidData.desc = "id-aa-signingCertificateV2"; + aOidData.mechanism = CKM_SHA_1; + aOidData.supportedExtension = UNSUPPORTED_CERT_EXTENSION; + NSSCMSAttribute* pAttribute = CMSAttributeArray_FindAttrByOidData(pCMSSignerInfo->authAttr, &aOidData, PR_TRUE); + if (pAttribute) + rInformation.bHasSigningCertificate = true; + + SECItem* pContentInfoContentData = pCMSSignedData->contentInfo.content.data; + if (bNonDetached && pContentInfoContentData && pContentInfoContentData->data) + { + // Not a detached signature. + if (!std::memcmp(pActualResultBuffer, pContentInfoContentData->data, nMaxResultLen) && nActualResultLen == pContentInfoContentData->len) + rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED; + } + else + { + // Detached, the usual case. + SECItem aActualResultItem; + aActualResultItem.data = pActualResultBuffer; + aActualResultItem.len = nActualResultLen; + if (NSS_CMSSignerInfo_Verify(pCMSSignerInfo, &aActualResultItem, nullptr) == SECSuccess) + rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED; + } + + // Everything went fine + PORT_Free(pActualResultBuffer); + HASH_Destroy(pHASHContext); + NSS_CMSSignerInfo_Destroy(pCMSSignerInfo); + for (auto pDocumentCertificate : aDocumentCertificates) + CERT_DestroyCertificate(pDocumentCertificate); + + return true; +#elif defined SVL_CRYPTO_MSCRYPTO + // Open a message for decoding. + HCRYPTMSG hMsg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + CMSG_DETACHED_FLAG, + 0, + NULL, + nullptr, + nullptr); + if (!hMsg) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgOpenToDecode() failed"); + return false; + } + + // Update the message with the encoded header blob. + if (!CryptMsgUpdate(hMsg, aSignature.data(), aSignature.size(), TRUE)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the header failed: " << WindowsErrorString(GetLastError())); + return false; + } + + // Update the message with the content blob. + for (const auto& rByteRange : aByteRanges) + { + rStream.Seek(rByteRange.first); + + const int nChunkLen = 4096; + std::vector<unsigned char> aBuffer(nChunkLen); + for (size_t nByte = 0; nByte < rByteRange.second;) + { + size_t nRemainingSize = rByteRange.second - nByte; + if (nRemainingSize < nChunkLen) + { + rStream.ReadBytes(aBuffer.data(), nRemainingSize); + if (!CryptMsgUpdate(hMsg, aBuffer.data(), nRemainingSize, FALSE)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the content failed: " << WindowsErrorString(GetLastError())); + return false; + } + nByte = rByteRange.second; + } + else + { + rStream.ReadBytes(aBuffer.data(), nChunkLen); + if (!CryptMsgUpdate(hMsg, aBuffer.data(), nChunkLen, FALSE)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the content failed: " << WindowsErrorString(GetLastError())); + return false; + } + nByte += nChunkLen; + } + } + } + if (!CryptMsgUpdate(hMsg, nullptr, 0, TRUE)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the last content failed: " << WindowsErrorString(GetLastError())); + return false; + } + + // Get the CRYPT_ALGORITHM_IDENTIFIER from the message. + DWORD nDigestID = 0; + if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_HASH_ALGORITHM_PARAM, 0, nullptr, &nDigestID)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError())); + return false; + } + std::unique_ptr<BYTE[]> pDigestBytes(new BYTE[nDigestID]); + if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_HASH_ALGORITHM_PARAM, 0, pDigestBytes.get(), &nDigestID)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError())); + return false; + } + auto pDigestID = reinterpret_cast<CRYPT_ALGORITHM_IDENTIFIER*>(pDigestBytes.get()); + if (OString(szOID_NIST_sha256) == pDigestID->pszObjId) + rInformation.nDigestID = xml::crypto::DigestID::SHA256; + else if (OString(szOID_RSA_SHA1RSA) == pDigestID->pszObjId || OString(szOID_OIWSEC_sha1) == pDigestID->pszObjId) + rInformation.nDigestID = xml::crypto::DigestID::SHA1; + else + // Don't error out here, we can still verify the message digest correctly, just the digest ID won't be set. + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: unhandled algorithm identifier '"<<pDigestID->pszObjId<<"'"); + + // Get the signer CERT_INFO from the message. + DWORD nSignerCertInfo = 0; + if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, nullptr, &nSignerCertInfo)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed"); + return false; + } + std::unique_ptr<BYTE[]> pSignerCertInfoBuf(new BYTE[nSignerCertInfo]); + if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, pSignerCertInfoBuf.get(), &nSignerCertInfo)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed"); + return false; + } + PCERT_INFO pSignerCertInfo = reinterpret_cast<PCERT_INFO>(pSignerCertInfoBuf.get()); + + // Open a certificate store in memory using CERT_STORE_PROV_MSG, which + // initializes it with the certificates from the message. + HCERTSTORE hStoreHandle = CertOpenStore(CERT_STORE_PROV_MSG, + PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + NULL, + 0, + hMsg); + if (!hStoreHandle) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CertOpenStore() failed"); + return false; + } + + // Find the signer's certificate in the store. + PCCERT_CONTEXT pSignerCertContext = CertGetSubjectCertificateFromStore(hStoreHandle, + PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + pSignerCertInfo); + if (!pSignerCertContext) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CertGetSubjectCertificateFromStore() failed"); + return false; + } + else + { + // Write rInformation.ouX509Certificate. + uno::Sequence<sal_Int8> aDerCert(pSignerCertContext->cbCertEncoded); + for (size_t i = 0; i < pSignerCertContext->cbCertEncoded; ++i) + aDerCert[i] = pSignerCertContext->pbCertEncoded[i]; + OUStringBuffer aBuffer; + comphelper::Base64::encode(aBuffer, aDerCert); + rInformation.ouX509Certificate = aBuffer.makeStringAndClear(); + } + + if (bNonDetached) + { + // Not a detached signature. + DWORD nContentParam = 0; + if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, nullptr, &nContentParam)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed"); + return false; + } + + std::vector<BYTE> aContentParam(nContentParam); + if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, aContentParam.data(), &nContentParam)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed"); + return false; + } + + if (VerifyNonDetachedSignature(rStream, aByteRanges, aContentParam)) + rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED; + } + else + { + // Detached, the usual case. + // Use the CERT_INFO from the signer certificate to verify the signature. + if (CryptMsgControl(hMsg, 0, CMSG_CTRL_VERIFY_SIGNATURE, pSignerCertContext->pCertInfo)) + rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED; + } + + // Check if we have a signing certificate attribute. + DWORD nSignedAttributes = 0; + if (CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, nullptr, &nSignedAttributes)) + { + std::unique_ptr<BYTE[]> pSignedAttributesBuf(new BYTE[nSignedAttributes]); + if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, pSignedAttributesBuf.get(), &nSignedAttributes)) + { + SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed"); + return false; + } + auto pSignedAttributes = reinterpret_cast<PCRYPT_ATTRIBUTES>(pSignedAttributesBuf.get()); + for (size_t nAttr = 0; nAttr < pSignedAttributes->cAttr; ++nAttr) + { + CRYPT_ATTRIBUTE& rAttr = pSignedAttributes->rgAttr[nAttr]; + /* + * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::= + * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) + * smime(16) id-aa(2) 47 } + */ + if (OString("1.2.840.113549.1.9.16.2.47") == rAttr.pszObjId) + { + rInformation.bHasSigningCertificate = true; + break; + } + } + } + + CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG); + CryptMsgClose(hMsg); + return true; +#else + // Not implemented. + (void)rStream; + (void)aByteRanges; + (void)bNonDetected; + (void)aSignature; + (void)rInformation; +static_assert(false, "WHAT!!!"); + return false; +#endif +} + } } |