[Libreoffice-commits] core.git: 4 commits - xmlsecurity/inc xmlsecurity/qa xmlsecurity/source

Miklos Vajna vmiklos at collabora.co.uk
Wed Oct 26 18:10:17 UTC 2016


 xmlsecurity/inc/pdfio/pdfdocument.hxx                  |   10 +
 xmlsecurity/qa/unit/pdfsigning/data/2good.pdf          |binary
 xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx          |  113 ++++++++++++++---
 xmlsecurity/source/helper/documentsignaturemanager.cxx |    7 -
 xmlsecurity/source/helper/pdfsignaturehelper.cxx       |    3 
 xmlsecurity/source/pdfio/pdfdocument.cxx               |   54 ++++++--
 xmlsecurity/source/pdfio/pdfverify.cxx                 |    3 
 7 files changed, 155 insertions(+), 35 deletions(-)

New commits:
commit b6f98b71155d3c7af70bfc623cfaa7da0fbb905f
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Wed Oct 26 17:55:02 2016 +0200

    xmlsecurity PDF verify: support SHA-256
    
    And various other minor fixes.
    
    Change-Id: Ifcccebf48aac8ad526406f2d7a402a840d3c91cd

diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx
index f42350a..8ef7afd 100644
--- a/xmlsecurity/inc/pdfio/pdfdocument.hxx
+++ b/xmlsecurity/inc/pdfio/pdfdocument.hxx
@@ -75,6 +75,8 @@ public:
     static size_t FindStartXRef(SvStream& rStream);
     void ReadXRef(SvStream& rStream);
     static void SkipWhitespace(SvStream& rStream);
+    /// Instead of all whitespace, just skip CR and NL characters.
+    static void SkipLineBreaks(SvStream& rStream);
     size_t GetObjectOffset(size_t nIndex) const;
     const std::vector< std::unique_ptr<PDFElement> >& GetElements();
     std::vector<PDFObjectElement*> GetPages();
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index a1ac63c..df7a809 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -18,6 +18,7 @@
 #include <comphelper/processfactory.hxx>
 #include <comphelper/scopeguard.hxx>
 #include <comphelper/string.hxx>
+#include <filter/msfilter/mscodec.hxx>
 #include <rtl/strbuf.hxx>
 #include <rtl/string.hxx>
 #include <sal/log.hxx>
@@ -784,7 +785,7 @@ bool PDFDocument::Tokenize(SvStream& rStream, bool bPartial)
                         return false;
                     }
 
-                    PDFDocument::SkipWhitespace(rStream);
+                    PDFDocument::SkipLineBreaks(rStream);
                     m_aElements.push_back(std::unique_ptr<PDFElement>(new PDFStreamElement(nLength)));
                     if (!m_aElements.back()->Read(rStream))
                         return false;
@@ -822,7 +823,7 @@ bool PDFDocument::Tokenize(SvStream& rStream, bool bPartial)
                 }
                 else
                 {
-                    SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Tokenize: unexpected '" << aKeyword << "' keyword");
+                    SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Tokenize: unexpected '" << aKeyword << "' keyword at byte position " << rStream.Tell());
                     return false;
                 }
             }
@@ -830,7 +831,7 @@ bool PDFDocument::Tokenize(SvStream& rStream, bool bPartial)
             {
                 if (!isspace(ch))
                 {
-                    SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Tokenize: unexpected character: " << ch);
+                    SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Tokenize: unexpected character: " << ch << " at byte position " << rStream.Tell());
                     return false;
                 }
             }
@@ -1034,6 +1035,24 @@ void PDFDocument::SkipWhitespace(SvStream& rStream)
     }
 }
 
+void PDFDocument::SkipLineBreaks(SvStream& rStream)
+{
+    char ch = 0;
+
+    while (true)
+    {
+        rStream.ReadChar(ch);
+        if (rStream.IsEof())
+            break;
+
+        if (ch != '\n' && ch != '\r')
+        {
+            rStream.SeekRel(-1);
+            return;
+        }
+    }
+}
+
 size_t PDFDocument::GetObjectOffset(size_t nIndex) const
 {
     auto it = m_aXRef.find(nIndex);
@@ -1388,7 +1407,10 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat
     switch (SECOID_FindOIDTag(&aAlgorithm))
     {
     case SEC_OID_SHA1:
-        nMaxResultLen = 20;
+        nMaxResultLen = msfilter::SHA1_HASH_LENGTH;
+        break;
+    case SEC_OID_SHA256:
+        nMaxResultLen = msfilter::SHA256_HASH_LENGTH;
         break;
     default:
         SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: unrecognized algorithm");
@@ -1519,14 +1541,14 @@ bool PDFNumberElement::Read(SvStream& rStream)
     m_nOffset = rStream.Tell();
     char ch;
     rStream.ReadChar(ch);
-    if (!isdigit(ch) && ch != '-')
+    if (!isdigit(ch) && ch != '-' && ch != '.')
     {
         rStream.SeekRel(-1);
         return false;
     }
     while (!rStream.IsEof())
     {
-        if (!isdigit(ch) && ch != '-')
+        if (!isdigit(ch) && ch != '-' && ch != '.')
         {
             rStream.SeekRel(-1);
             m_nLength = rStream.Tell() - m_nOffset;
@@ -1599,25 +1621,28 @@ const OString& PDFHexStringElement::GetValue() const
 
 bool PDFLiteralStringElement::Read(SvStream& rStream)
 {
-    char ch;
+    char nPrevCh = 0;
+    char ch = 0;
     rStream.ReadChar(ch);
     if (ch != '(')
     {
         SAL_INFO("xmlsecurity.pdfio", "PDFHexStringElement::Read: expected '(' as first character");
         return false;
     }
+    nPrevCh = ch;
     rStream.ReadChar(ch);
 
     OStringBuffer aBuf;
     while (!rStream.IsEof())
     {
-        if (ch == ')')
+        if (ch == ')' && nPrevCh != '\\')
         {
             m_aValue = aBuf.makeStringAndClear();
             SAL_INFO("xmlsecurity.pdfio", "PDFLiteralStringElement::Read: m_aValue is '" << m_aValue << "'");
             return true;
         }
         aBuf.append(ch);
+        nPrevCh = ch;
         rStream.ReadChar(ch);
     }
 
commit fc56d31c094f1e01adc5eca69b414e984c7e4baf
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Wed Oct 26 17:54:26 2016 +0200

    xmlsecurity PDF verify: fix handling of non-imported certs
    
    Previously we only managed to verify a signature in case the certificate
    was already imported in the local NSS db. Don't depend on that by
    (temporarily) importing certificates from the PDF signature.
    
    Also adjust a test file that failed previously (the test DB has only an
    "Alice" cert imported, intentionally sign the file as "Bob" as well).
    
    Change-Id: Id8440acc31915f5a1718ea48129b950bb67e7486

diff --git a/xmlsecurity/qa/unit/pdfsigning/data/2good.pdf b/xmlsecurity/qa/unit/pdfsigning/data/2good.pdf
index af668fc..10528c5 100644
Binary files a/xmlsecurity/qa/unit/pdfsigning/data/2good.pdf and b/xmlsecurity/qa/unit/pdfsigning/data/2good.pdf differ
diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
index 4442ac5..1f9ef83 100644
--- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
+++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
@@ -214,6 +214,9 @@ void PDFSigningTest::testPDFRemoveAll()
     aManager.mxSignatureStream = xStream;
     aManager.read(/*bUseTempStream=*/false);
     std::vector<SignatureInformation>& rInformations = aManager.maCurrentSignatureInformations;
+    // This was 1 when NSS_CMSSignerInfo_GetSigningCertificate() failed, which
+    // means that we only used the locally imported certificates for
+    // verification, not the ones provided in the PDF signature data.
     CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
 
     // Request removal of the first signature, should imply removal of the
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 20bbbbf..a1ac63c 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -1334,6 +1334,13 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat
         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
+    // perviously.
+    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)
     {
@@ -1456,6 +1463,8 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat
     PORT_Free(pActualResultBuffer);
     HASH_Destroy(pHASHContext);
     NSS_CMSSignerInfo_Destroy(pCMSSignerInfo);
+    for (auto pDocumentCertificate : aDocumentCertificates)
+        CERT_DestroyCertificate(pDocumentCertificate);
 
     return true;
 #else
commit 23ca39a7c2cd5b33ac6361282432c6f34c458366
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Wed Oct 26 17:53:04 2016 +0200

    xmlsecurity PDF sign: fix removing non-last signatures
    
    PDF signatures are always chained, so when removing a signature not only
    the item at a given position should be removed on the UI, but the whole
    position-last range.
    
    Change-Id: I76b14308885267cdac994fa957218a8b7df6b3cf

diff --git a/xmlsecurity/qa/unit/pdfsigning/data/2good.pdf b/xmlsecurity/qa/unit/pdfsigning/data/2good.pdf
new file mode 100644
index 0000000..af668fc
Binary files /dev/null and b/xmlsecurity/qa/unit/pdfsigning/data/2good.pdf differ
diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
index a2980db..4442ac5 100644
--- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
+++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
@@ -12,7 +12,10 @@
 #include <comphelper/processfactory.hxx>
 #include <osl/file.hxx>
 #include <test/bootstrapfixture.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/ucbstreamhelper.hxx>
 
+#include <documentsignaturemanager.hxx>
 #include <pdfio/pdfdocument.hxx>
 
 using namespace com::sun::star;
@@ -42,13 +45,16 @@ public:
     void testPDFAdd();
     /// Test signing a previously unsigned file twice.
     void testPDFAdd2();
-    /// Test remove a signature from a previously signed file.
+    /// Test removing a signature from a previously signed file.
     void testPDFRemove();
+    /// Test removing all signatures from a previously multi-signed file.
+    void testPDFRemoveAll();
 
     CPPUNIT_TEST_SUITE(PDFSigningTest);
     CPPUNIT_TEST(testPDFAdd);
     CPPUNIT_TEST(testPDFAdd2);
     CPPUNIT_TEST(testPDFRemove);
+    CPPUNIT_TEST(testPDFRemoveAll);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -187,6 +193,38 @@ void PDFSigningTest::testPDFRemove()
     }
 #endif
 }
+
+void PDFSigningTest::testPDFRemoveAll()
+{
+#ifndef _WIN32
+    // Make sure that good2.pdf has 2 valid signatures.  Unlike in
+    // testPDFRemove(), here intentionally test DocumentSignatureManager and
+    // PDFSignatureHelper code as well.
+    uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(mxComponentContext);
+    uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
+
+    // Copy the test document to a temporary file, as it'll be modified.
+    OUString aTargetDir = m_directories.getURLFromWorkdir("/CppunitTest/xmlsecurity_signing.test.user/");
+    OUString aOutURL = aTargetDir + "remove-all.pdf";
+    CPPUNIT_ASSERT_EQUAL(osl::File::RC::E_None, osl::File::copy(m_directories.getURLFromSrc(DATA_DIRECTORY) + "2good.pdf", aOutURL));
+    // Load the test document as a storage and read its two signatures.
+    DocumentSignatureManager aManager(mxComponentContext, SignatureModeDocumentContent);
+    SvStream* pStream = utl::UcbStreamHelper::CreateStream(aOutURL, StreamMode::READ | StreamMode::WRITE);
+    uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
+    aManager.mxSignatureStream = xStream;
+    aManager.read(/*bUseTempStream=*/false);
+    std::vector<SignatureInformation>& rInformations = aManager.maCurrentSignatureInformations;
+    CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
+
+    // Request removal of the first signature, should imply removal of the
+    // second chained signature as well.
+    aManager.remove(0);
+    // This was 2, Manager didn't write anything to disk when removal succeeded
+    // (instead of doing that when removal failed).
+    // Then this was 1, when the chained signature wasn't removed.
+    CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(0), rInformations.size());
+#endif
+}
 CPPUNIT_TEST_SUITE_REGISTRATION(PDFSigningTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/xmlsecurity/source/helper/documentsignaturemanager.cxx b/xmlsecurity/source/helper/documentsignaturemanager.cxx
index 0a5568f..dee86c8 100644
--- a/xmlsecurity/source/helper/documentsignaturemanager.cxx
+++ b/xmlsecurity/source/helper/documentsignaturemanager.cxx
@@ -317,14 +317,15 @@ void DocumentSignatureManager::remove(sal_uInt16 nPosition)
     {
         // Something not ZIP based, try PDF.
         uno::Reference<io::XInputStream> xInputStream(mxSignatureStream, uno::UNO_QUERY);
-        if (PDFSignatureHelper::RemoveSignature(xInputStream, nPosition))
+        if (!PDFSignatureHelper::RemoveSignature(xInputStream, nPosition))
         {
             SAL_WARN("xmlsecurity.helper", "PDFSignatureHelper::RemoveSignature() failed");
             return;
         }
 
-        // Only erase when the removal was successfull, it may fail for PDF.
-        maCurrentSignatureInformations.erase(maCurrentSignatureInformations.begin() + nPosition);
+        // Only erase when the removal was successful, it may fail for PDF.
+        // Also, erase the requested and all following signatures, as PDF signatures are always chained.
+        maCurrentSignatureInformations.erase(maCurrentSignatureInformations.begin() + nPosition, maCurrentSignatureInformations.end());
         return;
     }
 
commit f45ace2897dca1dd8f3553e46415df4fe7ad62d8
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Wed Oct 26 17:52:28 2016 +0200

    xmlsecurity PDF signing: fix byte range check for multiple signatures
    
    We can mandate that the byte range end is the end of the file for the
    last signature only.
    
    With this, signing a previously unsigned file multiple times works, so
    add a matching testcase for that as well.
    
    Change-Id: I8fe5482890fca4dab8da6305aa7fc7f60df612d8

diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx
index 59c7f5d..f42350a 100644
--- a/xmlsecurity/inc/pdfio/pdfdocument.hxx
+++ b/xmlsecurity/inc/pdfio/pdfdocument.hxx
@@ -88,8 +88,12 @@ public:
     /// Serializes the contents of the edit buffer.
     bool Write(SvStream& rStream);
     std::vector<PDFObjectElement*> GetSignatureWidgets();
-    /// Return value is about if we can determine a result, rInformation is about the actual result.
-    static bool ValidateSignature(SvStream& rStream, PDFObjectElement* pSignature, SignatureInformation& rInformation);
+    /**
+     * @param rInformation The actual result.
+     * @param bLast If this is the last signature in the file, so it covers the whole file physically.
+     * @return If we can determinate a result.
+     */
+    static bool ValidateSignature(SvStream& rStream, PDFObjectElement* pSignature, SignatureInformation& rInformation, bool bLast);
     /// Remove the nth signature from read document in the edit buffer.
     bool RemoveSignature(size_t nPosition);
 };
diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
index 6a3800f..a2980db 100644
--- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
+++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
@@ -29,17 +29,25 @@ class PDFSigningTest : public test::BootstrapFixture
 {
     uno::Reference<uno::XComponentContext> mxComponentContext;
 
+    /**
+     * Sign rInURL once and save the result as rOutURL, asserting that rInURL
+     * had nOriginalSignatureCount signatures.
+     */
+    void sign(const OUString& rInURL, const OUString& rOutURL, size_t nOriginalSignatureCount);
 public:
     PDFSigningTest();
     void setUp() override;
 
     /// Test adding a new signature to a previously unsigned file.
     void testPDFAdd();
+    /// Test signing a previously unsigned file twice.
+    void testPDFAdd2();
     /// Test remove a signature from a previously signed file.
     void testPDFRemove();
 
     CPPUNIT_TEST_SUITE(PDFSigningTest);
     CPPUNIT_TEST(testPDFAdd);
+    CPPUNIT_TEST(testPDFAdd2);
     CPPUNIT_TEST(testPDFRemove);
     CPPUNIT_TEST_SUITE_END();
 };
@@ -67,25 +75,20 @@ void PDFSigningTest::setUp()
 #endif
 }
 
-void PDFSigningTest::testPDFAdd()
+void PDFSigningTest::sign(const OUString& rInURL, const OUString& rOutURL, size_t nOriginalSignatureCount)
 {
-#ifndef _WIN32
-    // Make sure that no.pdf has no signatures.
+    // Make sure that input has nOriginalSignatureCount signatures.
     uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(mxComponentContext);
     uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
     xmlsecurity::pdfio::PDFDocument aDocument;
     {
-        OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
-        OUString aInURL = aSourceDir + "no.pdf";
-        SvFileStream aStream(aInURL, StreamMode::READ);
+        SvFileStream aStream(rInURL, StreamMode::READ);
         CPPUNIT_ASSERT(aDocument.Read(aStream));
         std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
-        CPPUNIT_ASSERT(aSignatures.empty());
+        CPPUNIT_ASSERT_EQUAL(nOriginalSignatureCount, aSignatures.size());
     }
 
-    // Sign it and write out the result as add.pdf.
-    OUString aTargetDir = m_directories.getURLFromWorkdir("/CppunitTest/xmlsecurity_signing.test.user/");
-    OUString aOutURL = aTargetDir + "add.pdf";
+    // Sign it and write out the result.
     {
         uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
         uno::Sequence<uno::Reference<security::XCertificate>> aCertificates = xSecurityEnvironment->getPersonalCertificates();
@@ -95,21 +98,54 @@ void PDFSigningTest::testPDFAdd()
             return;
         }
         CPPUNIT_ASSERT(aDocument.Sign(aCertificates[0], "test"));
-        SvFileStream aOutStream(aOutURL, StreamMode::WRITE | StreamMode::TRUNC);
+        SvFileStream aOutStream(rOutURL, StreamMode::WRITE | StreamMode::TRUNC);
         CPPUNIT_ASSERT(aDocument.Write(aOutStream));
     }
 
     // Read back the signed pdf and make sure that it has one valid signature.
     {
-        SvFileStream aStream(aOutURL, StreamMode::READ);
+        SvFileStream aStream(rOutURL, StreamMode::READ);
         xmlsecurity::pdfio::PDFDocument aVerifyDocument;
         CPPUNIT_ASSERT(aVerifyDocument.Read(aStream));
         std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aVerifyDocument.GetSignatureWidgets();
-        // This was 0 when PDFDocument::Sign() silently returned success, without doing anything.
-        CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aSignatures.size());
-        SignatureInformation aInfo(0);
-        CPPUNIT_ASSERT(xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[0], aInfo));
+        // This was nOriginalSignatureCount when PDFDocument::Sign() silently returned success, without doing anything.
+        CPPUNIT_ASSERT_EQUAL(nOriginalSignatureCount + 1, aSignatures.size());
+        for (size_t i = 0; i < aSignatures.size(); ++i)
+        {
+            SignatureInformation aInfo(i);
+            bool bLast = i == aSignatures.size() - 1;
+            CPPUNIT_ASSERT(xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[i], aInfo, bLast));
+        }
     }
+}
+
+void PDFSigningTest::testPDFAdd()
+{
+#ifndef _WIN32
+    OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
+    OUString aInURL = aSourceDir + "no.pdf";
+    OUString aTargetDir = m_directories.getURLFromWorkdir("/CppunitTest/xmlsecurity_signing.test.user/");
+    OUString aOutURL = aTargetDir + "add.pdf";
+    sign(aInURL, aOutURL, 0);
+#endif
+}
+
+void PDFSigningTest::testPDFAdd2()
+{
+#ifndef _WIN32
+    // Sign.
+    OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
+    OUString aInURL = aSourceDir + "no.pdf";
+    OUString aTargetDir = m_directories.getURLFromWorkdir("/CppunitTest/xmlsecurity_signing.test.user/");
+    OUString aOutURL = aTargetDir + "add.pdf";
+    sign(aInURL, aOutURL, 0);
+
+    // Sign again.
+    aInURL = aTargetDir + "add.pdf";
+    aOutURL = aTargetDir + "add2.pdf";
+    // This failed with "second range end is not the end of the file" for the
+    // first signature.
+    sign(aInURL, aOutURL, 1);
 #endif
 }
 
@@ -128,7 +164,7 @@ void PDFSigningTest::testPDFRemove()
         std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
         CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aSignatures.size());
         SignatureInformation aInfo(0);
-        CPPUNIT_ASSERT(xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[0], aInfo));
+        CPPUNIT_ASSERT(xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[0], aInfo, /*bLast=*/true));
     }
 
     // Remove the signature and write out the result as remove.pdf.
diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
index a1b1d20..859a479 100644
--- a/xmlsecurity/source/helper/pdfsignaturehelper.cxx
+++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
@@ -58,7 +58,8 @@ bool PDFSignatureHelper::ReadAndVerifySignature(const uno::Reference<io::XInputS
     {
         SignatureInformation aInfo(i);
 
-        if (!xmlsecurity::pdfio::PDFDocument::ValidateSignature(*pStream, aSignatures[i], aInfo))
+        bool bLast = i == aSignatures.size() - 1;
+        if (!xmlsecurity::pdfio::PDFDocument::ValidateSignature(*pStream, aSignatures[i], aInfo, bLast))
         {
             SAL_WARN("xmlsecurity.helper", "failed to determine digest match");
             continue;
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 7b8cea0..20bbbbf 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -1188,7 +1188,7 @@ std::vector<unsigned char> PDFDocument::DecodeHexString(PDFHexStringElement* pEl
     return aRet;
 }
 
-bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignature, SignatureInformation& rInformation)
+bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignature, SignatureInformation& rInformation, bool bLast)
 {
     PDFObjectElement* pValue = pSignature->LookupObject("V");
     if (!pValue)
@@ -1285,7 +1285,7 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat
     }
     rStream.Seek(STREAM_SEEK_TO_END);
     size_t nFileEnd = rStream.Tell();
-    if ((aByteRanges[1].first + aByteRanges[1].second) != nFileEnd)
+    if (bLast && (aByteRanges[1].first + aByteRanges[1].second) != nFileEnd)
     {
         SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: second range end is not the end of the file");
         return false;
diff --git a/xmlsecurity/source/pdfio/pdfverify.cxx b/xmlsecurity/source/pdfio/pdfverify.cxx
index f980db8..8e4ba42 100644
--- a/xmlsecurity/source/pdfio/pdfverify.cxx
+++ b/xmlsecurity/source/pdfio/pdfverify.cxx
@@ -113,7 +113,8 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(nArgc, pArgv)
             for (size_t i = 0; i < aSignatures.size(); ++i)
             {
                 SignatureInformation aInfo(i);
-                if (!xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[i], aInfo))
+                bool bLast = i == aSignatures.size() - 1;
+                if (!xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[i], aInfo, bLast))
                 {
                     SAL_WARN("xmlsecurity.pdfio", "failed to determine digest match");
                     return 1;


More information about the Libreoffice-commits mailing list