vcl/Library_vcl.mk | 2 vcl/source/gdi/pdfwriter_impl.cxx | 208 ++++++++++++++++++++++++++++++-------- 2 files changed, 171 insertions(+), 39 deletions(-)
New commits: commit 27d7aea00d22ad3fcdff2e7b267be1cf5c28d43c Author: Tor Lillqvist <t...@collabora.com> Date: Thu Feb 19 13:57:52 2015 +0200 tdf#84881: Work in progress: Perform the RFC3161 interaction with the TSA Use libcurl to perform the request and get the response. Improve error messages (only use SAL_WARN, though, so sadly not visible to end-users). Still to do: Decode the response and attach it to the signature. Implement request encoding and response decoding for Windows. I probably should extend (and rename) the HashContextScope class to handle all resources that need explicit deallocation, instead of calling curl_slist_free_all(), curl_easy_cleanup() and SECITEM_FreeItem() in so many places. The error handling of the PDF export functionality would need to be re-designed so that we could show actual error messages to the user instead of generic "signing failed" ones. But that is typical for much of our code... Change-Id: I6288de3f09021f8e0f385870143fefffbac2a706 diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index e8df725..e114ae3 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -59,6 +59,8 @@ $(eval $(call gb_Library_use_custom_headers,vcl,\ )) $(eval $(call gb_Library_use_externals,vcl,\ + $(if $(filter LINUX MACOSX,$(OS)), \ + curl) \ jpeg \ $(if $(filter-out WNT,$(OS)), \ nss3 \ diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 2f74f94..bb0c3f5 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -77,6 +77,9 @@ #include "sechash.h" #include "cms.h" #include "cmst.h" + +// We use curl for RFC3161 time stamp requests +#include <curl/curl.h> #endif #ifdef _WIN32 @@ -6163,12 +6166,20 @@ const SEC_ASN1Template TimeStampReq_Template[] = { 0, 0, 0, 0 } }; +size_t AppendToBuffer(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + OStringBuffer *pBuffer = reinterpret_cast<OStringBuffer*>(userdata); + pBuffer->append(ptr, size*nmemb); + + return size*nmemb; +} + #if 0 { #endif } // anonymous namespace -#endif +#endif // !defined(ANDROID) && !defined(IOS) && !defined(_WIN32) #ifdef _WIN32 @@ -6345,19 +6356,121 @@ bool PDFWriterImpl::finalizeSignature() src.extensions = NULL; - SECItem* item = SEC_ASN1EncodeItem(NULL, NULL, &src, TimeStampReq_Template); - SAL_INFO("vcl.pdfwriter", "item=" << item << " data=" << (item ? (void*)item->data : nullptr) << " len=" << (item ? item->len : -1)); + SECItem* timestamp_request = SEC_ASN1EncodeItem(NULL, NULL, &src, TimeStampReq_Template); + if (timestamp_request == NULL) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: SEC_ASN1EncodeItem failed"); + return false; + } + + if (timestamp_request->data == NULL) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: SEC_ASN1EncodeItem succeeded but got NULL data"); + SECITEM_FreeItem(timestamp_request, PR_TRUE); + return false; + } + + SAL_INFO("vcl.pdfwriter", "request len=" << (timestamp_request ? timestamp_request->len : -1)); #ifdef DBG_UTIL - if (item && item->data) { FILE *out = fopen("PDFWRITER.timestampreq.data", "wb"); - fwrite(item->data, item->len, 1, out); + fwrite(timestamp_request->data, timestamp_request->len, 1, out); + fclose(out); + } +#endif + + // Send time stamp request to TSA server, receive response + + CURL* curl = curl_easy_init(); + struct curl_slist* slist = NULL; + + if (!curl) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_init failed"); + SECITEM_FreeItem(timestamp_request, PR_TRUE); + return false; + } + + SAL_INFO("vcl.pdfwriter", "Setting curl to verbose: " << (curl_easy_setopt(curl, CURLOPT_VERBOSE, 1) == CURLE_OK ? "OK" : "FAIL")); + + if (curl_easy_setopt(curl, CURLOPT_URL, OUStringToOString(m_aContext.SignTSA, RTL_TEXTENCODING_UTF8).getStr()) != CURLE_OK) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_setopt(CURLOPT_URL) failed"); + curl_easy_cleanup(curl); + SECITEM_FreeItem(timestamp_request, PR_TRUE); + return false; + } + + slist = curl_slist_append(slist, "Content-Type: application/timestamp-query"); + slist = curl_slist_append(slist, "Accept: application/timestamp-reply"); + + if (curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist) != CURLE_OK) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_setopt(CURLOPT_HTTPHEADER) failed"); + curl_slist_free_all(slist); + curl_easy_cleanup(curl); + SECITEM_FreeItem(timestamp_request, PR_TRUE); + return false; + } + + if (curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, timestamp_request->len) != CURLE_OK || + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, timestamp_request->data) != CURLE_OK) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_setopt(CURLOPT_POSTFIELDSIZE or CURLOPT_POSTFIELDS) failed"); + curl_easy_cleanup(curl); + SECITEM_FreeItem(timestamp_request, PR_TRUE); + return false; + } + + OStringBuffer reply_buffer; + + if (curl_easy_setopt(curl, CURLOPT_WRITEDATA, &reply_buffer) != CURLE_OK || + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, AppendToBuffer) != CURLE_OK) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_setopt(CURLOPT_WRITEDATA or CURLOPT_WRITEFUNCTION) failed"); + curl_easy_cleanup(curl); + SECITEM_FreeItem(timestamp_request, PR_TRUE); + return false; + } + + if (curl_easy_setopt(curl, CURLOPT_POST, 1) != CURLE_OK) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_setopt(CURLOPT_POST) failed"); + curl_easy_cleanup(curl); + SECITEM_FreeItem(timestamp_request, PR_TRUE); + return false; + } + + char error_buffer[CURL_ERROR_SIZE]; + if (curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer) != CURLE_OK) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_setopt(CURLOPT_ERRORBUFFER) failed"); + curl_easy_cleanup(curl); + SECITEM_FreeItem(timestamp_request, PR_TRUE); + return false; + } + + if (curl_easy_perform(curl) != CURLE_OK) + { + SAL_WARN("vcl.pdfwriter", "PDF signing: curl_easy_perform failed: " << error_buffer); + curl_easy_cleanup(curl); + SECITEM_FreeItem(timestamp_request, PR_TRUE); + return false; + } + +#ifdef DBG_UTIL + { + FILE *out = fopen("PDFWRITER.reply.data", "wb"); + fwrite(reply_buffer.getStr(), reply_buffer.getLength(), 1, out); fclose(out); } #endif - SECITEM_FreeItem(item, PR_TRUE); + curl_slist_free_all(slist); + curl_easy_cleanup(curl); + + SECITEM_FreeItem(timestamp_request, PR_TRUE); } if (NSS_CMSSignerInfo_IncludeCerts(cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) commit 2ddfaa6d323b5db2f59f06f7708c5209549abeee Author: Tor Lillqvist <t...@collabora.com> Date: Thu Feb 19 11:34:52 2015 +0200 tdf#84881: reqPolicy and certReq are optional Change-Id: Ia5687bf2d68eef06aeb618d5387c663807d24560 diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 4865918..2f74f94 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -6156,9 +6156,9 @@ const SEC_ASN1Template TimeStampReq_Template[] = { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(TimeStampReq) }, { SEC_ASN1_INTEGER, offsetof(TimeStampReq, version), 0, 0 }, { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(TimeStampReq, messageImprint), SEC_ASN1_SUB(MessageImprint_Template), 0 }, - { SEC_ASN1_OBJECT_ID, offsetof(TimeStampReq, reqPolicy), 0, 0 }, + { SEC_ASN1_OBJECT_ID | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, reqPolicy), 0, 0 }, { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, nonce), 0, 0 }, - { SEC_ASN1_BOOLEAN, offsetof(TimeStampReq, certReq), 0, 0 }, + { SEC_ASN1_BOOLEAN | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, certReq), 0, 0 }, { SEC_ASN1_XTRN | SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(TimeStampReq, extensions), SEC_ASN1_SUB(Extensions_Template), 0 }, { 0, 0, 0, 0 } }; @@ -6339,10 +6339,9 @@ bool PDFWriterImpl::finalizeSignature() src.nonce.data = reinterpret_cast<unsigned char*>(&nNonce); src.nonce.len = sizeof(nNonce); - unsigned char cFalse = false; - src.certReq.type = siUnsignedInteger; - src.certReq.data = &cFalse; - src.certReq.len = sizeof(cFalse); + src.certReq.type = siBuffer; + src.certReq.data = NULL; + src.certReq.len = 0; src.extensions = NULL; commit 159a4c3c75e3a7aecbf1656f3254331892098ba7 Author: Tor Lillqvist <t...@collabora.com> Date: Thu Feb 19 11:08:33 2015 +0200 tdf#84881: WiP: Fill in more fields of the TimeStampReq Use the digestAlg in the NSSCMSSignerInfo, once we have it, as hashAlgorithm. Use a random number as nonce. Temporarily, dump the TimeStampReq object to a file for inspection in a DBG_UTIL build. Change-Id: I696271b3ccc6cef86a70bc78f86d6eae27a4af77 diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index edd69c9..4865918 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -34,6 +34,7 @@ #include <com/sun/star/util/URL.hpp> #include <com/sun/star/util/URLTransformer.hpp> #include <comphelper/processfactory.hxx> +#include <comphelper/random.hxx> #include <comphelper/string.hxx> #include <cppuhelper/implbase1.hxx> #include <i18nlangtag/languagetag.hxx> @@ -6039,6 +6040,13 @@ public: }; /* +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + -- contains a value of the type + -- registered for use with the + -- algorithm object identifier value + MessageImprint ::= SEQUENCE { hashAlgorithm AlgorithmIdentifier, hashedMessage OCTET STRING } @@ -6080,6 +6088,8 @@ typedef struct { } Accuracy; /* +TSAPolicyId ::= OBJECT IDENTIFIER + TimeStampReq ::= SEQUENCE { version INTEGER { v1(1) }, messageImprint MessageImprint, @@ -6271,42 +6281,6 @@ bool PDFWriterImpl::finalizeSignature() HASH_End(hc.get(), digest.data, &digest.len, SHA1_LENGTH); hc.clear(); - TimeStampReq src; - - unsigned char cOne = 1; - src.version.type = siUnsignedInteger; - src.version.data = &cOne; - src.version.len = sizeof(cOne); - - // FIXME, use proper contents - src.messageImprint.hashAlgorithm.algorithm.type = siBuffer; - src.messageImprint.hashAlgorithm.algorithm.data = NULL; - src.messageImprint.hashAlgorithm.algorithm.len = 0; - src.messageImprint.hashAlgorithm.parameters.type = siBuffer; - src.messageImprint.hashAlgorithm.parameters.data = NULL; - src.messageImprint.hashAlgorithm.parameters.len = 0; - src.messageImprint.hashedMessage = digest; - - src.reqPolicy.type = siBuffer; - src.reqPolicy.data = NULL; - src.reqPolicy.len = 0; - - // FIXME, need a proper nonce - src.nonce.type = siBuffer; - src.nonce.data = NULL; - src.nonce.len = 0; - - unsigned char cFalse = false; - src.certReq.type = siUnsignedInteger; - src.certReq.data = &cFalse; - src.certReq.len = sizeof(cFalse); - - src.extensions = NULL; - - SECItem* item = SEC_ASN1EncodeItem(NULL, NULL, &src, TimeStampReq_Template); - SAL_INFO("vcl.pdfwriter", "item=" << item << " data=" << (item ? (void*)item->data : nullptr) << " len=" << (item ? item->len : -1)); - SECITEM_FreeItem(item, PR_TRUE); - NSSCMSMessage *cms_msg = NSS_CMSMessage_Create(NULL); if (!cms_msg) { @@ -6343,6 +6317,50 @@ bool PDFWriterImpl::finalizeSignature() return false; } + // Now we have the hash algorithm as a SECItem available in cms_siger->digestAlg + if( !m_aContext.SignTSA.isEmpty() ) + { + TimeStampReq src; + + unsigned char cOne = 1; + src.version.type = siUnsignedInteger; + src.version.data = &cOne; + src.version.len = sizeof(cOne); + + src.messageImprint.hashAlgorithm = cms_signer->digestAlg; + src.messageImprint.hashedMessage = digest; + + src.reqPolicy.type = siBuffer; + src.reqPolicy.data = NULL; + src.reqPolicy.len = 0; + + unsigned int nNonce = comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32); + src.nonce.type = siUnsignedInteger; + src.nonce.data = reinterpret_cast<unsigned char*>(&nNonce); + src.nonce.len = sizeof(nNonce); + + unsigned char cFalse = false; + src.certReq.type = siUnsignedInteger; + src.certReq.data = &cFalse; + src.certReq.len = sizeof(cFalse); + + src.extensions = NULL; + + SECItem* item = SEC_ASN1EncodeItem(NULL, NULL, &src, TimeStampReq_Template); + SAL_INFO("vcl.pdfwriter", "item=" << item << " data=" << (item ? (void*)item->data : nullptr) << " len=" << (item ? item->len : -1)); + +#ifdef DBG_UTIL + if (item && item->data) + { + FILE *out = fopen("PDFWRITER.timestampreq.data", "wb"); + fwrite(item->data, item->len, 1, out); + fclose(out); + } +#endif + + SECITEM_FreeItem(item, PR_TRUE); + } + if (NSS_CMSSignerInfo_IncludeCerts(cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) { SAL_WARN("vcl.pdfwriter", "PDF signing: can't include cert chain."); _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits