include/sal/log-areas.dox                        |    4 
 xmlsecurity/Library_xmlsecurity.mk               |    1 
 xmlsecurity/inc/pdfio/pdfdocument.hxx            |   40 --
 xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx    |    5 
 xmlsecurity/source/helper/pdfsignaturehelper.cxx |  313 +++++++++++++++++++++
 xmlsecurity/source/pdfio/pdfdocument.cxx         |  335 -----------------------
 xmlsecurity/workben/pdfverify.cxx                |   18 -
 7 files changed, 323 insertions(+), 393 deletions(-)

New commits:
commit 2dd3d03b5350e18cb5f39978022620b55d3d8c5b
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu Sep 24 21:06:46 2020 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Sep 25 09:02:57 2020 +0200

    xmlsecurity: fold pdfio into pdfsignaturehelper
    
    Most of the initial pdfio was moved to vcl as vcl::filter::PDFDocument.
    A small part was left here, because it depended on NSS.  Then later the
    NSS bits were moved to svl::crypto::Signing.  The rest is just a small
    amount of code, keeping that separate from PDFSignatureHelper, which is
    its only user makes little sense.
    
    With this, vcl::filter::PDFDocument is an implementation detail of
    PDFSignatureHelper during signature verification.
    
    Change-Id: I6230f9e46deeff7159970f88dbb3bd2de0e9ce7d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103350
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 7a740191ffdf..2d3a974055e7 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -572,8 +572,8 @@ certain functionality.
 @li @c xmlsecurity.dialogs - xml security dialogs
 @li @c xmlsecurity.helper
 @li @c xmlsecurity.ooxml - OOXML signature support
-@li @c xmlsecurity.pdfio - signing of existing PDF
-@li @c xmlsecurity.pdfio.test
+@li @c xmlsecurity.qa
+@li @c xmlsecurity.workben
 @li @c xmlsecurity.xmlsec - xmlsec wrapper
 @li @c xmlsecurity.xmlsec.gpg - gpg xmlsec component
 
diff --git a/xmlsecurity/Library_xmlsecurity.mk 
b/xmlsecurity/Library_xmlsecurity.mk
index 89306bf5ac27..f50140edb303 100644
--- a/xmlsecurity/Library_xmlsecurity.mk
+++ b/xmlsecurity/Library_xmlsecurity.mk
@@ -75,7 +75,6 @@ $(eval $(call gb_Library_add_exception_objects,xmlsecurity,\
        xmlsecurity/source/helper/xsecparser \
        xmlsecurity/source/helper/xsecsign \
        xmlsecurity/source/helper/xsecverify \
-       xmlsecurity/source/pdfio/pdfdocument \
 ))
 
 $(eval $(call gb_Library_use_externals,xmlsecurity,\
diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx 
b/xmlsecurity/inc/pdfio/pdfdocument.hxx
deleted file mode 100644
index 4e36978ee009..000000000000
--- a/xmlsecurity/inc/pdfio/pdfdocument.hxx
+++ /dev/null
@@ -1,40 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#ifndef INCLUDED_XMLSECURITY_INC_PDFIO_PDFDOCUMENT_HXX
-#define INCLUDED_XMLSECURITY_INC_PDFIO_PDFDOCUMENT_HXX
-
-#include <xmlsecuritydllapi.h>
-
-namespace vcl::filter
-{
-class PDFObjectElement;
-class PDFDocument;
-}
-struct SignatureInformation;
-class SvStream;
-
-namespace xmlsecurity::pdfio
-{
-/**
- * @param rInformation The actual result.
- * @param rDocument the parsed document to see if the signature is partial.
- * @return If we can determinate a result.
- */
-XMLSECURITY_DLLPUBLIC bool ValidateSignature(SvStream& rStream,
-                                             vcl::filter::PDFObjectElement* 
pSignature,
-                                             SignatureInformation& 
rInformation,
-                                             vcl::filter::PDFDocument& 
rDocument);
-
-} // namespace xmlsecurity::pdfio
-
-#endif // INCLUDED_XMLSECURITY_INC_PDFIO_PDFDOCUMENT_HXX
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx 
b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
index 8b4a610fa56c..586ef54d3075 100644
--- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
+++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
@@ -127,7 +127,7 @@ bool PDFSigningTest::sign(const OUString& rInURL, const 
OUString& rOutURL,
             // Only try certificates that are already active and not expired
             if ((now > aNotValidAfter) || (now < aNotValidBefore))
             {
-                SAL_WARN("xmlsecurity.pdfio.test",
+                SAL_WARN("xmlsecurity.qa",
                          "Skipping a certificate that is not yet valid or 
already not valid");
             }
             else
@@ -139,8 +139,7 @@ bool PDFSigningTest::sign(const OUString& rInURL, const 
OUString& rOutURL,
                     DWORD dwErr = GetLastError();
                     if (HRESULT_FROM_WIN32(dwErr) == CRYPT_E_NO_KEY_PROPERTY)
                     {
-                        SAL_WARN("xmlsecurity.pdfio.test",
-                                 "Skipping a certificate without a private 
key");
+                        SAL_WARN("xmlsecurity.qa", "Skipping a certificate 
without a private key");
                         continue; // The certificate does not have a private 
key - not a valid certificate
                     }
                 }
diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx 
b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
index b49cdd3e449f..f9bb5342c626 100644
--- a/xmlsecurity/source/helper/pdfsignaturehelper.cxx
+++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
@@ -30,8 +30,11 @@
 #include <unotools/streamwrap.hxx>
 #include <unotools/ucbstreamhelper.hxx>
 #include <vcl/filter/pdfdocument.hxx>
-
-#include <pdfio/pdfdocument.hxx>
+#include <vcl/checksum.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <svl/cryptosign.hxx>
+#include <config_features.h>
+#include <vcl/filter/PDFiumLibrary.hxx>
 
 using namespace ::com::sun::star;
 
@@ -112,6 +115,310 @@ void GetSignatureLineShape(const 
uno::Reference<frame::XModel>& xModel, sal_Int3
     rSignatureLineShape = std::vector<sal_Int8>(aStream.GetSize());
     aStream.ReadBytes(rSignatureLineShape.data(), rSignatureLineShape.size());
 }
+
+/// Turns an array of floats into offset + length pairs.
+bool GetByteRangesFromPDF(vcl::filter::PDFArrayElement& rArray,
+                          std::vector<std::pair<size_t, size_t>>& rByteRanges)
+{
+    size_t nByteRangeOffset = 0;
+    const std::vector<vcl::filter::PDFElement*>& rByteRangeElements = 
rArray.GetElements();
+    for (size_t i = 0; i < rByteRangeElements.size(); ++i)
+    {
+        auto pNumber = 
dynamic_cast<vcl::filter::PDFNumberElement*>(rByteRangeElements[i]);
+        if (!pNumber)
+        {
+            SAL_WARN("xmlsecurity.helper",
+                     "ValidateSignature: signature offset and length has to be 
a number");
+            return false;
+        }
+
+        if (i % 2 == 0)
+        {
+            nByteRangeOffset = pNumber->GetValue();
+            continue;
+        }
+        size_t nByteRangeLength = pNumber->GetValue();
+        rByteRanges.emplace_back(nByteRangeOffset, nByteRangeLength);
+    }
+
+    return true;
+}
+
+/// Determines the last position that is covered by a signature.
+bool GetEOFOfSignature(vcl::filter::PDFObjectElement* pSignature, size_t& rEOF)
+{
+    vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
+    if (!pValue)
+    {
+        return false;
+    }
+
+    auto pByteRange = 
dynamic_cast<vcl::filter::PDFArrayElement*>(pValue->Lookup("ByteRange"));
+    if (!pByteRange || pByteRange->GetElements().size() < 2)
+    {
+        return false;
+    }
+
+    std::vector<std::pair<size_t, size_t>> aByteRanges;
+    if (!GetByteRangesFromPDF(*pByteRange, aByteRanges))
+    {
+        return false;
+    }
+
+    rEOF = aByteRanges[1].first + aByteRanges[1].second;
+    return true;
+}
+
+/// Checks if there are unsigned incremental updates between the signatures or 
after the last one.
+bool IsCompleteSignature(SvStream& rStream, vcl::filter::PDFDocument& 
rDocument,
+                         vcl::filter::PDFObjectElement* pSignature)
+{
+    std::set<size_t> aSignedEOFs;
+    for (const auto& i : rDocument.GetSignatureWidgets())
+    {
+        size_t nEOF = 0;
+        if (!GetEOFOfSignature(i, nEOF))
+        {
+            return false;
+        }
+
+        aSignedEOFs.insert(nEOF);
+    }
+
+    size_t nSignatureEOF = 0;
+    if (!GetEOFOfSignature(pSignature, nSignatureEOF))
+    {
+        return false;
+    }
+
+    const std::vector<size_t>& rAllEOFs = rDocument.GetEOFs();
+    bool bFoundOwn = false;
+    for (const auto& rEOF : rAllEOFs)
+    {
+        if (rEOF == nSignatureEOF)
+        {
+            bFoundOwn = true;
+            continue;
+        }
+
+        if (!bFoundOwn)
+        {
+            continue;
+        }
+
+        if (aSignedEOFs.find(rEOF) == aSignedEOFs.end())
+        {
+            // Unsigned incremental update found.
+            return false;
+        }
+    }
+
+    // Make sure we find the incremental update of the signature itself.
+    if (!bFoundOwn)
+    {
+        return false;
+    }
+
+    // No additional content after the last incremental update.
+    rStream.Seek(STREAM_SEEK_TO_END);
+    size_t nFileEnd = rStream.Tell();
+    return std::find(rAllEOFs.begin(), rAllEOFs.end(), nFileEnd) != 
rAllEOFs.end();
+}
+
+#if HAVE_FEATURE_PDFIUM
+/// Collects the checksum of each page of one version of the PDF.
+void AnalyizeSignatureStream(SvMemoryStream& rStream, 
std::vector<BitmapChecksum>& rPageChecksums)
+{
+    auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+    vcl::pdf::PDFiumDocument aPdfDocument(
+        FPDF_LoadMemDocument(rStream.GetData(), rStream.GetSize(), 
/*password=*/nullptr));
+
+    int nPageCount = aPdfDocument.getPageCount();
+    for (int nPage = 0; nPage < nPageCount; ++nPage)
+    {
+        std::unique_ptr<vcl::pdf::PDFiumPage> 
pPdfPage(aPdfDocument.openPage(nPage));
+        if (!pPdfPage)
+        {
+            return;
+        }
+
+        BitmapChecksum nPageChecksum = pPdfPage->getChecksum();
+        rPageChecksums.push_back(nPageChecksum);
+    }
+}
+#endif
+
+/**
+ * Checks if incremental updates after singing performed valid modifications 
only.
+ * Annotations/commenting is OK, other changes are not.
+ */
+bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* 
pSignature)
+{
+    size_t nSignatureEOF = 0;
+    if (!GetEOFOfSignature(pSignature, nSignatureEOF))
+    {
+        return false;
+    }
+
+#if HAVE_FEATURE_PDFIUM
+    SvMemoryStream aSignatureStream;
+    sal_uInt64 nPos = rStream.Tell();
+    rStream.Seek(0);
+    aSignatureStream.WriteStream(rStream, nSignatureEOF);
+    rStream.Seek(nPos);
+    aSignatureStream.Seek(0);
+    std::vector<BitmapChecksum> aSignedPages;
+    AnalyizeSignatureStream(aSignatureStream, aSignedPages);
+
+    SvMemoryStream aFullStream;
+    nPos = rStream.Tell();
+    rStream.Seek(0);
+    aFullStream.WriteStream(rStream);
+    rStream.Seek(nPos);
+    aFullStream.Seek(0);
+    std::vector<BitmapChecksum> aAllPages;
+    AnalyizeSignatureStream(aFullStream, aAllPages);
+
+    // Fail if any page looks different after signing and at the end. 
Annotations/commenting doesn't
+    // count, though.
+    return aSignedPages == aAllPages;
+#else
+    (void)rStream;
+    return true;
+#endif
+}
+
+/**
+ * @param rInformation The actual result.
+ * @param rDocument the parsed document to see if the signature is partial.
+ * @return If we can determinate a result.
+ */
+bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* 
pSignature,
+                       SignatureInformation& rInformation, 
vcl::filter::PDFDocument& rDocument)
+{
+    vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
+    if (!pValue)
+    {
+        SAL_WARN("xmlsecurity.helper", "ValidateSignature: no value");
+        return false;
+    }
+
+    auto pContents = 
dynamic_cast<vcl::filter::PDFHexStringElement*>(pValue->Lookup("Contents"));
+    if (!pContents)
+    {
+        SAL_WARN("xmlsecurity.helper", "ValidateSignature: no contents");
+        return false;
+    }
+
+    auto pByteRange = 
dynamic_cast<vcl::filter::PDFArrayElement*>(pValue->Lookup("ByteRange"));
+    if (!pByteRange || pByteRange->GetElements().size() < 2)
+    {
+        SAL_WARN("xmlsecurity.helper", "ValidateSignature: no byte range or 
too few elements");
+        return false;
+    }
+
+    auto pSubFilter = 
dynamic_cast<vcl::filter::PDFNameElement*>(pValue->Lookup("SubFilter"));
+    const bool bNonDetached = pSubFilter && pSubFilter->GetValue() == 
"adbe.pkcs7.sha1";
+    if (!pSubFilter
+        || (pSubFilter->GetValue() != "adbe.pkcs7.detached" && !bNonDetached
+            && pSubFilter->GetValue() != "ETSI.CAdES.detached"))
+    {
+        if (!pSubFilter)
+            SAL_WARN("xmlsecurity.helper", "ValidateSignature: missing 
sub-filter");
+        else
+            SAL_WARN("xmlsecurity.helper", "ValidateSignature: unsupported 
sub-filter: '"
+                                               << pSubFilter->GetValue() << 
"'");
+        return false;
+    }
+
+    // Reason / comment / description is optional.
+    auto pReason = 
dynamic_cast<vcl::filter::PDFHexStringElement*>(pValue->Lookup("Reason"));
+    if (pReason)
+    {
+        // See appendUnicodeTextString() for the export equivalent of this.
+        std::vector<unsigned char> aReason = 
vcl::filter::PDFDocument::DecodeHexString(pReason);
+        OUStringBuffer aBuffer;
+        sal_uInt16 nByte = 0;
+        for (size_t i = 0; i < aReason.size(); ++i)
+        {
+            if (i % 2 == 0)
+                nByte = aReason[i];
+            else
+            {
+                sal_Unicode nUnicode;
+                nUnicode = (nByte << 8);
+                nUnicode |= aReason[i];
+                aBuffer.append(nUnicode);
+            }
+        }
+
+        if (!aBuffer.isEmpty())
+            rInformation.ouDescription = aBuffer.makeStringAndClear();
+    }
+
+    // Date: used only when the time of signing is not available in the
+    // signature.
+    auto pM = 
dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pValue->Lookup("M"));
+    if (pM)
+    {
+        // Example: "D:20161027100104".
+        const OString& rM = pM->GetValue();
+        if (rM.startsWith("D:") && rM.getLength() >= 16)
+        {
+            rInformation.stDateTime.Year = rM.copy(2, 4).toInt32();
+            rInformation.stDateTime.Month = rM.copy(6, 2).toInt32();
+            rInformation.stDateTime.Day = rM.copy(8, 2).toInt32();
+            rInformation.stDateTime.Hours = rM.copy(10, 2).toInt32();
+            rInformation.stDateTime.Minutes = rM.copy(12, 2).toInt32();
+            rInformation.stDateTime.Seconds = rM.copy(14, 2).toInt32();
+        }
+    }
+
+    // Build a list of offset-length pairs, representing the signed bytes.
+    std::vector<std::pair<size_t, size_t>> aByteRanges;
+    if (!GetByteRangesFromPDF(*pByteRange, aByteRanges))
+    {
+        return false;
+    }
+
+    // Detect if the byte ranges don't cover everything, but the signature 
itself.
+    if (aByteRanges.size() < 2)
+    {
+        SAL_WARN("xmlsecurity.helper", "ValidateSignature: expected 2 byte 
ranges");
+        return false;
+    }
+    if (aByteRanges[0].first != 0)
+    {
+        SAL_WARN("xmlsecurity.helper", "ValidateSignature: first range start 
is not 0");
+        return false;
+    }
+    // 2 is the leading "<" and the trailing ">" around the hex string.
+    size_t nSignatureLength = 
static_cast<size_t>(pContents->GetValue().getLength()) + 2;
+    if (aByteRanges[1].first != (aByteRanges[0].second + nSignatureLength))
+    {
+        SAL_WARN("xmlsecurity.helper",
+                 "ValidateSignature: second range start is not the end of the 
signature");
+        return false;
+    }
+    rInformation.bPartialDocumentSignature = !IsCompleteSignature(rStream, 
rDocument, pSignature);
+    if (!IsValidSignature(rStream, pSignature))
+    {
+        SAL_WARN("xmlsecurity.helper", "ValidateSignature: invalid incremental 
update detected");
+        return false;
+    }
+
+    // At this point there is no obviously missing info to validate the
+    // signature.
+    std::vector<unsigned char> aSignature = 
vcl::filter::PDFDocument::DecodeHexString(pContents);
+    if (aSignature.empty())
+    {
+        SAL_WARN("xmlsecurity.helper", "ValidateSignature: empty contents");
+        return false;
+    }
+
+    return svl::crypto::Signing::Verify(rStream, aByteRanges, bNonDetached, 
aSignature,
+                                        rInformation);
+}
 }
 
 PDFSignatureHelper::PDFSignatureHelper() = default;
@@ -148,7 +455,7 @@ bool 
PDFSignatureHelper::ReadAndVerifySignatureSvStream(SvStream& rStream)
     {
         SignatureInformation aInfo(i);
 
-        if (!xmlsecurity::pdfio::ValidateSignature(rStream, aSignatures[i], 
aInfo, aDocument))
+        if (!ValidateSignature(rStream, aSignatures[i], aInfo, aDocument))
             SAL_WARN("xmlsecurity.helper", "failed to determine digest match");
 
         m_aSignatureInfos.push_back(aInfo);
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx 
b/xmlsecurity/source/pdfio/pdfdocument.cxx
deleted file mode 100644
index b2c1cc0db77a..000000000000
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ /dev/null
@@ -1,335 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-#include <pdfio/pdfdocument.hxx>
-
-#include <memory>
-#include <vector>
-
-#include <config_features.h>
-
-#include <vcl/filter/PDFiumLibrary.hxx>
-#include <rtl/string.hxx>
-#include <rtl/ustrbuf.hxx>
-#include <sal/log.hxx>
-#include <sal/types.h>
-
-#include <svl/sigstruct.hxx>
-#include <svl/cryptosign.hxx>
-#include <vcl/filter/pdfdocument.hxx>
-
-using namespace com::sun::star;
-
-namespace
-{
-/// Turns an array of floats into offset + length pairs.
-bool GetByteRangesFromPDF(vcl::filter::PDFArrayElement& rArray,
-                          std::vector<std::pair<size_t, size_t>>& rByteRanges)
-{
-    size_t nByteRangeOffset = 0;
-    const std::vector<vcl::filter::PDFElement*>& rByteRangeElements = 
rArray.GetElements();
-    for (size_t i = 0; i < rByteRangeElements.size(); ++i)
-    {
-        auto pNumber = 
dynamic_cast<vcl::filter::PDFNumberElement*>(rByteRangeElements[i]);
-        if (!pNumber)
-        {
-            SAL_WARN("xmlsecurity.pdfio",
-                     "ValidateSignature: signature offset and length has to be 
a number");
-            return false;
-        }
-
-        if (i % 2 == 0)
-        {
-            nByteRangeOffset = pNumber->GetValue();
-            continue;
-        }
-        size_t nByteRangeLength = pNumber->GetValue();
-        rByteRanges.emplace_back(nByteRangeOffset, nByteRangeLength);
-    }
-
-    return true;
-}
-
-/// Determines the last position that is covered by a signature.
-bool GetEOFOfSignature(vcl::filter::PDFObjectElement* pSignature, size_t& rEOF)
-{
-    vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
-    if (!pValue)
-    {
-        return false;
-    }
-
-    auto pByteRange = 
dynamic_cast<vcl::filter::PDFArrayElement*>(pValue->Lookup("ByteRange"));
-    if (!pByteRange || pByteRange->GetElements().size() < 2)
-    {
-        return false;
-    }
-
-    std::vector<std::pair<size_t, size_t>> aByteRanges;
-    if (!GetByteRangesFromPDF(*pByteRange, aByteRanges))
-    {
-        return false;
-    }
-
-    rEOF = aByteRanges[1].first + aByteRanges[1].second;
-    return true;
-}
-
-/// Checks if there are unsigned incremental updates between the signatures or 
after the last one.
-bool IsCompleteSignature(SvStream& rStream, vcl::filter::PDFDocument& 
rDocument,
-                         vcl::filter::PDFObjectElement* pSignature)
-{
-    std::set<size_t> aSignedEOFs;
-    for (const auto& i : rDocument.GetSignatureWidgets())
-    {
-        size_t nEOF = 0;
-        if (!GetEOFOfSignature(i, nEOF))
-        {
-            return false;
-        }
-
-        aSignedEOFs.insert(nEOF);
-    }
-
-    size_t nSignatureEOF = 0;
-    if (!GetEOFOfSignature(pSignature, nSignatureEOF))
-    {
-        return false;
-    }
-
-    const std::vector<size_t>& rAllEOFs = rDocument.GetEOFs();
-    bool bFoundOwn = false;
-    for (const auto& rEOF : rAllEOFs)
-    {
-        if (rEOF == nSignatureEOF)
-        {
-            bFoundOwn = true;
-            continue;
-        }
-
-        if (!bFoundOwn)
-        {
-            continue;
-        }
-
-        if (aSignedEOFs.find(rEOF) == aSignedEOFs.end())
-        {
-            // Unsigned incremental update found.
-            return false;
-        }
-    }
-
-    // Make sure we find the incremental update of the signature itself.
-    if (!bFoundOwn)
-    {
-        return false;
-    }
-
-    // No additional content after the last incremental update.
-    rStream.Seek(STREAM_SEEK_TO_END);
-    size_t nFileEnd = rStream.Tell();
-    return std::find(rAllEOFs.begin(), rAllEOFs.end(), nFileEnd) != 
rAllEOFs.end();
-}
-
-#if HAVE_FEATURE_PDFIUM
-/// Collects the checksum of each page of one version of the PDF.
-void AnalyizeSignatureStream(SvMemoryStream& rStream, 
std::vector<BitmapChecksum>& rPageChecksums)
-{
-    auto pPdfium = vcl::pdf::PDFiumLibrary::get();
-    vcl::pdf::PDFiumDocument aPdfDocument(
-        FPDF_LoadMemDocument(rStream.GetData(), rStream.GetSize(), 
/*password=*/nullptr));
-
-    int nPageCount = aPdfDocument.getPageCount();
-    for (int nPage = 0; nPage < nPageCount; ++nPage)
-    {
-        std::unique_ptr<vcl::pdf::PDFiumPage> 
pPdfPage(aPdfDocument.openPage(nPage));
-        if (!pPdfPage)
-        {
-            return;
-        }
-
-        BitmapChecksum nPageChecksum = pPdfPage->getChecksum();
-        rPageChecksums.push_back(nPageChecksum);
-    }
-}
-#endif
-
-/**
- * Checks if incremental updates after singing performed valid modifications 
only.
- * Annotations/commenting is OK, other changes are not.
- */
-bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* 
pSignature)
-{
-    size_t nSignatureEOF = 0;
-    if (!GetEOFOfSignature(pSignature, nSignatureEOF))
-    {
-        return false;
-    }
-
-#if HAVE_FEATURE_PDFIUM
-    SvMemoryStream aSignatureStream;
-    sal_uInt64 nPos = rStream.Tell();
-    rStream.Seek(0);
-    aSignatureStream.WriteStream(rStream, nSignatureEOF);
-    rStream.Seek(nPos);
-    aSignatureStream.Seek(0);
-    std::vector<BitmapChecksum> aSignedPages;
-    AnalyizeSignatureStream(aSignatureStream, aSignedPages);
-
-    SvMemoryStream aFullStream;
-    nPos = rStream.Tell();
-    rStream.Seek(0);
-    aFullStream.WriteStream(rStream);
-    rStream.Seek(nPos);
-    aFullStream.Seek(0);
-    std::vector<BitmapChecksum> aAllPages;
-    AnalyizeSignatureStream(aFullStream, aAllPages);
-
-    // Fail if any page looks different after signing and at the end. 
Annotations/commenting doesn't
-    // count, though.
-    return aSignedPages == aAllPages;
-#else
-    (void)rStream;
-    return true;
-#endif
-}
-}
-
-namespace xmlsecurity::pdfio
-{
-bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* 
pSignature,
-                       SignatureInformation& rInformation, 
vcl::filter::PDFDocument& rDocument)
-{
-    vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
-    if (!pValue)
-    {
-        SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no value");
-        return false;
-    }
-
-    auto pContents = 
dynamic_cast<vcl::filter::PDFHexStringElement*>(pValue->Lookup("Contents"));
-    if (!pContents)
-    {
-        SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no contents");
-        return false;
-    }
-
-    auto pByteRange = 
dynamic_cast<vcl::filter::PDFArrayElement*>(pValue->Lookup("ByteRange"));
-    if (!pByteRange || pByteRange->GetElements().size() < 2)
-    {
-        SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no byte range or too 
few elements");
-        return false;
-    }
-
-    auto pSubFilter = 
dynamic_cast<vcl::filter::PDFNameElement*>(pValue->Lookup("SubFilter"));
-    const bool bNonDetached = pSubFilter && pSubFilter->GetValue() == 
"adbe.pkcs7.sha1";
-    if (!pSubFilter
-        || (pSubFilter->GetValue() != "adbe.pkcs7.detached" && !bNonDetached
-            && pSubFilter->GetValue() != "ETSI.CAdES.detached"))
-    {
-        if (!pSubFilter)
-            SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: missing 
sub-filter");
-        else
-            SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: unsupported 
sub-filter: '"
-                                              << pSubFilter->GetValue() << 
"'");
-        return false;
-    }
-
-    // Reason / comment / description is optional.
-    auto pReason = 
dynamic_cast<vcl::filter::PDFHexStringElement*>(pValue->Lookup("Reason"));
-    if (pReason)
-    {
-        // See appendUnicodeTextString() for the export equivalent of this.
-        std::vector<unsigned char> aReason = 
vcl::filter::PDFDocument::DecodeHexString(pReason);
-        OUStringBuffer aBuffer;
-        sal_uInt16 nByte = 0;
-        for (size_t i = 0; i < aReason.size(); ++i)
-        {
-            if (i % 2 == 0)
-                nByte = aReason[i];
-            else
-            {
-                sal_Unicode nUnicode;
-                nUnicode = (nByte << 8);
-                nUnicode |= aReason[i];
-                aBuffer.append(nUnicode);
-            }
-        }
-
-        if (!aBuffer.isEmpty())
-            rInformation.ouDescription = aBuffer.makeStringAndClear();
-    }
-
-    // Date: used only when the time of signing is not available in the
-    // signature.
-    auto pM = 
dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pValue->Lookup("M"));
-    if (pM)
-    {
-        // Example: "D:20161027100104".
-        const OString& rM = pM->GetValue();
-        if (rM.startsWith("D:") && rM.getLength() >= 16)
-        {
-            rInformation.stDateTime.Year = rM.copy(2, 4).toInt32();
-            rInformation.stDateTime.Month = rM.copy(6, 2).toInt32();
-            rInformation.stDateTime.Day = rM.copy(8, 2).toInt32();
-            rInformation.stDateTime.Hours = rM.copy(10, 2).toInt32();
-            rInformation.stDateTime.Minutes = rM.copy(12, 2).toInt32();
-            rInformation.stDateTime.Seconds = rM.copy(14, 2).toInt32();
-        }
-    }
-
-    // Build a list of offset-length pairs, representing the signed bytes.
-    std::vector<std::pair<size_t, size_t>> aByteRanges;
-    if (!GetByteRangesFromPDF(*pByteRange, aByteRanges))
-    {
-        return false;
-    }
-
-    // Detect if the byte ranges don't cover everything, but the signature 
itself.
-    if (aByteRanges.size() < 2)
-    {
-        SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: expected 2 byte 
ranges");
-        return false;
-    }
-    if (aByteRanges[0].first != 0)
-    {
-        SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: first range start is 
not 0");
-        return false;
-    }
-    // 2 is the leading "<" and the trailing ">" around the hex string.
-    size_t nSignatureLength = 
static_cast<size_t>(pContents->GetValue().getLength()) + 2;
-    if (aByteRanges[1].first != (aByteRanges[0].second + nSignatureLength))
-    {
-        SAL_WARN("xmlsecurity.pdfio",
-                 "ValidateSignature: second range start is not the end of the 
signature");
-        return false;
-    }
-    rInformation.bPartialDocumentSignature = !IsCompleteSignature(rStream, 
rDocument, pSignature);
-    if (!IsValidSignature(rStream, pSignature))
-    {
-        SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: invalid incremental 
update detected");
-        return false;
-    }
-
-    // At this point there is no obviously missing info to validate the
-    // signature.
-    std::vector<unsigned char> aSignature = 
vcl::filter::PDFDocument::DecodeHexString(pContents);
-    if (aSignature.empty())
-    {
-        SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: empty contents");
-        return false;
-    }
-
-    return svl::crypto::Signing::Verify(rStream, aByteRanges, bNonDetached, 
aSignature,
-                                        rInformation);
-}
-
-} // namespace xmlsecurity::pdfio
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/workben/pdfverify.cxx 
b/xmlsecurity/workben/pdfverify.cxx
index 78595bae0ef9..d98a2fb1750e 100644
--- a/xmlsecurity/workben/pdfverify.cxx
+++ b/xmlsecurity/workben/pdfverify.cxx
@@ -58,7 +58,7 @@ int pdfVerify(int nArgc, char** pArgv)
 {
     if (nArgc < 2)
     {
-        SAL_WARN("xmlsecurity.pdfio", "not enough parameters");
+        SAL_WARN("xmlsecurity.workben", "not enough parameters");
         return 1;
     }
 
@@ -70,7 +70,7 @@ int pdfVerify(int nArgc, char** pArgv)
     }
     catch (const uno::RuntimeException&)
     {
-        TOOLS_WARN_EXCEPTION("xmlsecurity.pdfio",
+        TOOLS_WARN_EXCEPTION("xmlsecurity.workben",
                              "cppu::defaultBootstrap_InitialComponentContext() 
failed:");
         return 1;
     }
@@ -95,7 +95,7 @@ int pdfVerify(int nArgc, char** pArgv)
     }
     catch (const uno::DeploymentException&)
     {
-        TOOLS_WARN_EXCEPTION("xmlsecurity.pdfio",
+        TOOLS_WARN_EXCEPTION("xmlsecurity.workben",
                              "DeploymentException while creating 
SEInitializer:");
         return 1;
     }
@@ -142,7 +142,7 @@ int pdfVerify(int nArgc, char** pArgv)
     vcl::filter::PDFDocument aDocument;
     if (!aDocument.Read(aStream))
     {
-        SAL_WARN("xmlsecurity.pdfio", "failed to read the document");
+        SAL_WARN("xmlsecurity.workben", "failed to read the document");
         return 1;
     }
 
@@ -159,14 +159,14 @@ int pdfVerify(int nArgc, char** pArgv)
         size_t nPosition = aSignatures.size() - 1;
         if (!aDocument.RemoveSignature(nPosition))
         {
-            SAL_WARN("xmlsecurity.pdfio", "failed to remove signature #" << 
nPosition);
+            SAL_WARN("xmlsecurity.workben", "failed to remove signature #" << 
nPosition);
             return 1;
         }
 
         SvFileStream aOutStream(aOutURL, StreamMode::WRITE | 
StreamMode::TRUNC);
         if (!aDocument.Write(aOutStream))
         {
-            SAL_WARN("xmlsecurity.pdfio", "failed to write the document");
+            SAL_WARN("xmlsecurity.workben", "failed to write the document");
             return 1;
         }
 
@@ -180,19 +180,19 @@ int pdfVerify(int nArgc, char** pArgv)
         = xSecurityEnvironment->getPersonalCertificates();
     if (!aCertificates.hasElements())
     {
-        SAL_WARN("xmlsecurity.pdfio", "no signing certificates found");
+        SAL_WARN("xmlsecurity.workben", "no signing certificates found");
         return 1;
     }
     if (!aDocument.Sign(aCertificates[0], "pdfverify", /*bAdES=*/true))
     {
-        SAL_WARN("xmlsecurity.pdfio", "failed to sign");
+        SAL_WARN("xmlsecurity.workben", "failed to sign");
         return 1;
     }
 
     SvFileStream aOutStream(aOutURL, StreamMode::WRITE | StreamMode::TRUNC);
     if (!aDocument.Write(aOutStream))
     {
-        SAL_WARN("xmlsecurity.pdfio", "failed to write the document");
+        SAL_WARN("xmlsecurity.workben", "failed to write the document");
         return 1;
     }
 
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to