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

Miklos Vajna vmiklos at collabora.co.uk
Tue Oct 18 08:59:23 UTC 2016


 xmlsecurity/inc/pdfio/pdfdocument.hxx    |    8 ++
 xmlsecurity/source/pdfio/pdfdocument.cxx |  117 +++++++++++++++++++++++++++++--
 xmlsecurity/source/pdfio/pdfverify.cxx   |   62 ++++++++++++----
 3 files changed, 167 insertions(+), 20 deletions(-)

New commits:
commit 9a3b752756204307a0439c4e3534c094c6ee979d
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Tue Oct 18 09:21:05 2016 +0200

    xmlsecurity: initial incremental update support in pdfverify
    
    I plan to use this for signing purposes, but so far what's implemented
    just writes out an incremental update at the end of the file, without
    actually updating much (just an unreferenced appearance object).
    
    Change-Id: I1cb40430ade6af0a25ff914ba4df670a77fcf457

diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx
index 0eaaa09..2f95b7d 100644
--- a/xmlsecurity/inc/pdfio/pdfdocument.hxx
+++ b/xmlsecurity/inc/pdfio/pdfdocument.hxx
@@ -13,6 +13,8 @@
 
 #include <vector>
 
+#include <com/sun/star/security/XCertificate.hpp>
+
 #include <tools/stream.hxx>
 
 #include <xmlsecuritydllapi.h>
@@ -43,6 +45,8 @@ class XMLSECURITY_DLLPUBLIC PDFDocument
     // List of object offsets we know.
     std::vector<size_t> m_aXRef;
     PDFTrailerElement* m_pTrailer;
+    /// All editing takes place in this buffer, if it happens.
+    SvMemoryStream m_aEditBuffer;
 
     static int AsHex(char ch);
     /// Decode a hex dump.
@@ -61,6 +65,10 @@ public:
     std::vector<PDFObjectElement*> GetPages();
 
     bool Read(SvStream& rStream);
+    /// Sign the read document with xCertificate in the edit buffer.
+    bool Sign(const css::uno::Reference<css::security::XCertificate>& xCertificate);
+    /// 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);
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 8cd2b5c..0ab9976 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -121,6 +121,8 @@ public:
     double LookupNumber(SvStream& rStream) const;
     /// Lookup referenced object, without assuming anything about its contents.
     PDFObjectElement* LookupObject() const;
+    int GetObjectValue() const;
+    int GetGenerationValue() const;
 };
 
 /// Stream object: a byte array with a known length.
@@ -213,6 +215,92 @@ PDFDocument::PDFDocument()
 {
 }
 
+bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& /*xCertificate*/)
+{
+    m_aEditBuffer.WriteCharPtr("\n");
+
+    // Write appearance object.
+    size_t nAppearanceId = m_aXRef.size();
+    m_aXRef.push_back(m_aEditBuffer.Tell());
+    m_aEditBuffer.WriteUInt32AsString(nAppearanceId);
+    m_aEditBuffer.WriteCharPtr(" 0 obj\n");
+    m_aEditBuffer.WriteCharPtr("<</Type/XObject\n/Subtype/Form\n");
+    m_aEditBuffer.WriteCharPtr("/BBox[0 0 0 0]\n/Length 0\n>>\n");
+    m_aEditBuffer.WriteCharPtr("stream\n\nendstream\nendobj\n");
+
+    // Write the xref table.
+    sal_uInt64 nXRefOffset = m_aEditBuffer.Tell();
+    m_aEditBuffer.WriteCharPtr("\nxref\n0 ");
+    m_aEditBuffer.WriteUInt32AsString(m_aXRef.size());
+    m_aEditBuffer.WriteCharPtr("\n");
+    for (size_t nObject = 0; nObject < m_aXRef.size(); ++nObject)
+    {
+        OStringBuffer aBuffer;
+        aBuffer.append(static_cast<sal_Int32>(m_aXRef[nObject]));
+        while (aBuffer.getLength() < 10)
+            aBuffer.insert(0, "0");
+        if (nObject == 0)
+            aBuffer.append(" 65535 f\n");
+        else
+            aBuffer.append(" 00000 n\n");
+        m_aEditBuffer.WriteOString(aBuffer.toString());
+    }
+
+    // Write the trailer.
+    auto pRoot = dynamic_cast<PDFReferenceElement*>(m_pTrailer->Lookup("Root"));
+    if (!pRoot)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: trailer has no root reference");
+        return false;
+    }
+    m_aEditBuffer.WriteCharPtr("trailer\n<</Size ");
+    m_aEditBuffer.WriteUInt32AsString(m_aXRef.size());
+    m_aEditBuffer.WriteCharPtr("/Root ");
+    m_aEditBuffer.WriteUInt32AsString(pRoot->GetObjectValue());
+    m_aEditBuffer.WriteCharPtr(" ");
+    m_aEditBuffer.WriteUInt32AsString(pRoot->GetGenerationValue());
+    m_aEditBuffer.WriteCharPtr(" R\n");
+    if (auto pInfo = dynamic_cast<PDFReferenceElement*>(m_pTrailer->Lookup("Info")))
+    {
+        m_aEditBuffer.WriteCharPtr("/Info ");
+        m_aEditBuffer.WriteUInt32AsString(pInfo->GetObjectValue());
+        m_aEditBuffer.WriteCharPtr(" ");
+        m_aEditBuffer.WriteUInt32AsString(pInfo->GetGenerationValue());
+        m_aEditBuffer.WriteCharPtr(" R\n");
+    }
+    if (auto pID = dynamic_cast<PDFArrayElement*>(m_pTrailer->Lookup("ID")))
+    {
+        const std::vector<PDFElement*>& rElements = pID->GetElements();
+        m_aEditBuffer.WriteCharPtr("/ID [ <");
+        for (size_t i = 0; i < rElements.size(); ++i)
+        {
+            auto pIDString = dynamic_cast<PDFHexStringElement*>(rElements[i]);
+            if (!pIDString)
+                continue;
+
+            m_aEditBuffer.WriteOString(pIDString->GetValue());
+            if ((i + 1) < rElements.size())
+                m_aEditBuffer.WriteCharPtr(">\n<");
+        }
+        m_aEditBuffer.WriteCharPtr("> ]\n");
+    }
+    m_aEditBuffer.WriteCharPtr(">>\n");
+
+    // Write startxref.
+    m_aEditBuffer.WriteCharPtr("startxref\n");
+    m_aEditBuffer.WriteUInt32AsString(nXRefOffset);
+    m_aEditBuffer.WriteCharPtr("\n%%EOF\n");
+
+    return true;
+}
+
+bool PDFDocument::Write(SvStream& rStream)
+{
+    m_aEditBuffer.Seek(0);
+    rStream.WriteStream(m_aEditBuffer);
+    return rStream.good();
+}
+
 bool PDFDocument::Read(SvStream& rStream)
 {
     // Check file magic.
@@ -225,6 +313,10 @@ bool PDFDocument::Read(SvStream& rStream)
         return false;
     }
 
+    // Allow later editing of the contents in-memory.
+    rStream.Seek(0);
+    m_aEditBuffer.WriteStream(rStream);
+
     // Look up the offset of the xref table.
     size_t nStartXRef = FindStartXRef(rStream);
     SAL_INFO("xmlsecurity.pdfio", "PDFDocument::Read: nStartXRef is " << nStartXRef);
@@ -984,13 +1076,13 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat
     NSS_CMSSignerInfo_Destroy(pCMSSignerInfo);
 
     return true;
-#else
+#endif
+
     // Not implemented.
     (void)rStream;
     (void)rInformation;
 
     return false;
-#endif
 }
 
 bool PDFCommentElement::Read(SvStream& rStream)
@@ -1239,8 +1331,15 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
         auto pHexString = dynamic_cast<PDFHexStringElement*>(rElements[i].get());
         if (pHexString)
         {
-            rDictionary[aName] = pHexString;
-            aName.clear();
+            if (!pArray)
+            {
+                rDictionary[aName] = pHexString;
+                aName.clear();
+            }
+            else
+            {
+                pArray->PushBack(pHexString);
+            }
             continue;
         }
 
@@ -1397,6 +1496,16 @@ PDFObjectElement* PDFReferenceElement::LookupObject() const
     return nullptr;
 }
 
+int PDFReferenceElement::GetObjectValue() const
+{
+    return m_fObjectValue;
+}
+
+int PDFReferenceElement::GetGenerationValue() const
+{
+    return m_fGenerationValue;
+}
+
 bool PDFDictionaryElement::Read(SvStream& rStream)
 {
     char ch;
diff --git a/xmlsecurity/source/pdfio/pdfverify.cxx b/xmlsecurity/source/pdfio/pdfverify.cxx
index 30e085c..5df1fff 100644
--- a/xmlsecurity/source/pdfio/pdfverify.cxx
+++ b/xmlsecurity/source/pdfio/pdfverify.cxx
@@ -38,10 +38,13 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(nArgc, pArgv)
     uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xComponentContext);
     uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
 
-    OUString aURL;
-    osl::FileBase::getFileURLFromSystemPath(OUString::fromUtf8(pArgv[1]), aURL);
+    OUString aInURL;
+    osl::FileBase::getFileURLFromSystemPath(OUString::fromUtf8(pArgv[1]), aInURL);
+    OUString aOutURL;
+    if (nArgc > 2)
+        osl::FileBase::getFileURLFromSystemPath(OUString::fromUtf8(pArgv[2]), aOutURL);
 
-    SvFileStream aStream(aURL, StreamMode::READ);
+    SvFileStream aStream(aInURL, StreamMode::READ);
     xmlsecurity::pdfio::PDFDocument aDocument;
     if (!aDocument.Read(aStream))
     {
@@ -49,24 +52,51 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(nArgc, pArgv)
         return 1;
     }
 
-    std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
-    if (aSignatures.empty())
-        std::cerr << "found no signatures" << std::endl;
-    else
+    if (aOutURL.isEmpty())
     {
-        std::cerr << "found " << aSignatures.size() << " signatures" << std::endl;
-        for (size_t i = 0; i < aSignatures.size(); ++i)
+        // Verify.
+        std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
+        if (aSignatures.empty())
+            std::cerr << "found no signatures" << std::endl;
+        else
         {
-            SignatureInformation aInfo(i);
-            if (!xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[i], aInfo))
+            std::cerr << "found " << aSignatures.size() << " signatures" << std::endl;
+            for (size_t i = 0; i < aSignatures.size(); ++i)
             {
-                SAL_WARN("xmlsecurity.pdfio", "failed to determine digest match");
-                return 1;
-            }
+                SignatureInformation aInfo(i);
+                if (!xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[i], aInfo))
+                {
+                    SAL_WARN("xmlsecurity.pdfio", "failed to determine digest match");
+                    return 1;
+                }
 
-            bool bSuccess = aInfo.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
-            std::cerr << "signature #" << i << ": digest match? " << bSuccess << std::endl;
+                bool bSuccess = aInfo.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
+                std::cerr << "signature #" << i << ": digest match? " << bSuccess << std::endl;
+            }
         }
+
+        return 0;
+    }
+
+    // Sign.
+    uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
+    uno::Sequence<uno::Reference<security::XCertificate>> aCertificates = xSecurityEnvironment->getPersonalCertificates();
+    if (!aCertificates.hasElements())
+    {
+        SAL_WARN("xmlsecurity.pdfio", "no signing certificates found");
+        return 1;
+    }
+    if (!aDocument.Sign(aCertificates[0]))
+    {
+        SAL_WARN("xmlsecurity.pdfio", "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");
+        return 1;
     }
 
     return 0;


More information about the Libreoffice-commits mailing list