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

Miklos Vajna vmiklos at collabora.co.uk
Tue Oct 25 12:04:19 UTC 2016


 xmlsecurity/inc/pdfio/pdfdocument.hxx    |    8 +
 xmlsecurity/source/pdfio/pdfdocument.cxx |  175 +++++++++++++++++++++++++++++--
 2 files changed, 174 insertions(+), 9 deletions(-)

New commits:
commit d34a8993e3b3200de10d20990d14dc9288d8d6c7
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Tue Oct 25 11:33:21 2016 +0200

    xmlsecurity PDF sign: append reference to the Page's Annots key
    
    Previously we assumed that the Page object's dictionary has no Annots
    key. Now detect if that's not true, and in that case don't just copy of
    the whole dictionary (as part of the incremental update), instead copy
    it in two steps, so we can insert our reference in the middle.
    
    This is needed (but not enough alone) to be able to sign a PDF document
    multiple times.
    
    Change-Id: Ia5bf993320428eef80551e7e9cc7bfb2b858db7f
    Reviewed-on: https://gerrit.libreoffice.org/30257
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Tested-by: Jenkins <ci at libreoffice.org>

diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx
index b02bf93..3886eab 100644
--- a/xmlsecurity/inc/pdfio/pdfdocument.hxx
+++ b/xmlsecurity/inc/pdfio/pdfdocument.hxx
@@ -37,7 +37,13 @@ public:
     virtual ~PDFElement() { }
 };
 
-/// In-memory representation of an on-disk PDF document.
+/**
+ * In-memory representation of an on-disk PDF document.
+ *
+ * The PDF element list is not meant to be saved back to disk, but some
+ * elements remember their source offset / length, and based on that it's
+ * possible to modify the input file.
+ */
 class XMLSECURITY_DLLPUBLIC PDFDocument
 {
     /// This vector owns all elements.
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 947bacb..5778f20 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -59,11 +59,18 @@ public:
 /// Numbering object: an integer or a real.
 class PDFNumberElement : public PDFElement
 {
+    /// Input file start location.
+    sal_uInt64 m_nOffset;
+    /// Input file token length.
+    sal_uInt64 m_nLength;
     double m_fValue;
 
 public:
+    PDFNumberElement();
     bool Read(SvStream& rStream) override;
     double GetValue() const;
+    sal_uInt64 GetLocation() const;
+    sal_uInt64 GetLength() const;
 };
 
 class PDFReferenceElement;
@@ -79,6 +86,10 @@ class PDFObjectElement : public PDFElement
     sal_uInt64 m_nDictionaryOffset;
     /// Length of the dictionary buffer till (before) the '<<' token.
     sal_uInt64 m_nDictionaryLength;
+    /// Position after the '/' token.
+    std::map<OString, sal_uInt64> m_aDictionaryKeyOffset;
+    /// Length of the dictionary key and value, till (before) the next token.
+    std::map<OString, sal_uInt64> m_aDictionaryKeyValueLength;
 
 public:
     PDFObjectElement(PDFDocument& rDoc, double fObjectValue, double fGenerationValue);
@@ -91,12 +102,16 @@ public:
     sal_uInt64 GetDictionaryOffset();
     void SetDictionaryLength(sal_uInt64 nDictionaryLength);
     sal_uInt64 GetDictionaryLength();
+    void SetDictionaryKeyOffset(const OString& rKey, sal_uInt64 nOffset);
+    sal_uInt64 GetDictionaryKeyOffset(const OString& rKey) const;
+    void SetDictionaryKeyValueLength(const OString& rKey, sal_uInt64 nLength);
+    sal_uInt64 GetDictionaryKeyValueLength(const OString& rKey) const;
 };
 
 /// Dictionary object: a set key-value pairs.
 class PDFDictionaryElement : public PDFElement
 {
-    /// Offset after the '>>' token.
+    /// Offset after the '<<' token.
     sal_uInt64 m_nLocation;
 
 public:
@@ -122,9 +137,16 @@ public:
 class PDFNameElement : public PDFElement
 {
     OString m_aValue;
+    /// Offset after the '/' token.
+    sal_uInt64 m_nLocation;
+    /// Length till the next token start.
+    sal_uInt64 m_nLength;
 public:
+    PDFNameElement();
     bool Read(SvStream& rStream) override;
     const OString& GetValue() const;
+    sal_uInt64 GetLocation() const;
+    sal_uInt64 GetLength() const;
 };
 
 /// Reference object: something with a unique ID.
@@ -172,8 +194,11 @@ public:
 /// Array object: a list.
 class PDFArrayElement : public PDFElement
 {
+    /// Location after the '[' token.
+    sal_uInt64 m_nOffset;
     std::vector<PDFElement*> m_aElements;
 public:
+    PDFArrayElement();
     bool Read(SvStream& rStream) override;
     void PushBack(PDFElement* pElement);
     const std::vector<PDFElement*>& GetElements();
@@ -182,8 +207,12 @@ public:
 /// End of an array: ']'.
 class PDFEndArrayElement : public PDFElement
 {
+    /// Location before the ']' token.
+    sal_uInt64 m_nOffset;
 public:
+    PDFEndArrayElement();
     bool Read(SvStream& rStream) override;
+    sal_uInt64 GetOffset() const;
 };
 
 /// Boolean object: a 'true' or a 'false'.
@@ -355,11 +384,33 @@ bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& xCertificat
     m_aEditBuffer.WriteUInt32AsString(nFirstPageId);
     m_aEditBuffer.WriteCharPtr(" 0 obj\n");
     m_aEditBuffer.WriteCharPtr("<<");
-    m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), pFirstPage->GetDictionaryLength());
-    m_aEditBuffer.WriteCharPtr("/Annots[");
-    m_aEditBuffer.WriteUInt32AsString(nAnnotId);
-    m_aEditBuffer.WriteCharPtr(" 0 R]");
-    m_aEditBuffer.WriteCharPtr(">>\nendobj\n\n");
+    auto pAnnots = dynamic_cast<PDFArrayElement*>(pFirstPage->Lookup("Annots"));
+    if (!pAnnots)
+    {
+        // No Annots key, just write the key with a single reference.
+        m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), pFirstPage->GetDictionaryLength());
+        m_aEditBuffer.WriteCharPtr("/Annots[");
+        m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+        m_aEditBuffer.WriteCharPtr(" 0 R]");
+    }
+    else
+    {
+        // Annots key is already there, insert our reference at the end.
+
+        // Offset right before the end of the Annots array.
+        sal_uInt64 nAnnotsEndOffset = pFirstPage->GetDictionaryKeyOffset("Annots") + pFirstPage->GetDictionaryKeyValueLength("Annots") - 1;
+        // Length of beginning of the dictionary -> Annots end.
+        sal_uInt64 nAnnotsBeforeEndLength = nAnnotsEndOffset - pFirstPage->GetDictionaryOffset();
+        m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + pFirstPage->GetDictionaryOffset(), nAnnotsBeforeEndLength);
+        m_aEditBuffer.WriteCharPtr(" ");
+        m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+        m_aEditBuffer.WriteCharPtr(" 0 R");
+        // Length of Annots end -> end of the dictionary.
+        sal_uInt64 nAnnotsAfterEndLength = pFirstPage->GetDictionaryOffset() + pFirstPage->GetDictionaryLength() - nAnnotsEndOffset;
+        m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData()) + nAnnotsEndOffset, nAnnotsAfterEndLength);
+    }
+    m_aEditBuffer.WriteCharPtr(">>");
+    m_aEditBuffer.WriteCharPtr("\nendobj\n\n");
 
     // Write the updated Catalog object, references nAnnotId.
     auto pRoot = dynamic_cast<PDFReferenceElement*>(m_pTrailer->Lookup("Root"));
@@ -1349,9 +1400,17 @@ bool PDFCommentElement::Read(SvStream& rStream)
     return false;
 }
 
+PDFNumberElement::PDFNumberElement()
+    : m_nOffset(0),
+      m_nLength(0),
+      m_fValue(0)
+{
+}
+
 bool PDFNumberElement::Read(SvStream& rStream)
 {
     OStringBuffer aBuf;
+    m_nOffset = rStream.Tell();
     char ch;
     rStream.ReadChar(ch);
     while (!rStream.IsEof())
@@ -1359,6 +1418,7 @@ bool PDFNumberElement::Read(SvStream& rStream)
         if (!isdigit(ch) && ch != '-')
         {
             rStream.SeekRel(-1);
+            m_nLength = rStream.Tell() - m_nOffset;
             m_fValue = aBuf.makeStringAndClear().toDouble();
             SAL_INFO("xmlsecurity.pdfio", "PDFNumberElement::Read: m_fValue is '" << m_fValue << "'");
             return true;
@@ -1370,6 +1430,16 @@ bool PDFNumberElement::Read(SvStream& rStream)
     return false;
 }
 
+sal_uInt64 PDFNumberElement::GetLocation() const
+{
+    return m_nOffset;
+}
+
+sal_uInt64 PDFNumberElement::GetLength() const
+{
+    return m_nLength;
+}
+
 PDFBooleanElement::PDFBooleanElement(bool /*bValue*/)
 {
 }
@@ -1506,6 +1576,7 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
     }
 
     OString aName;
+    sal_uInt64 nNameOffset = 0;
     std::vector<PDFNumberElement*> aNumbers;
     // The array value we're in -- if any.
     PDFArrayElement* pArray = nullptr;
@@ -1541,7 +1612,13 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
         {
             if (!aNumbers.empty())
             {
-                rDictionary[aName] = aNumbers.back();
+                PDFNumberElement* pNumber = aNumbers.back();
+                rDictionary[aName] = pNumber;
+                if (pThisObject)
+                {
+                    pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+                    pThisObject->SetDictionaryKeyValueLength(aName, pNumber->GetLocation() + pNumber->GetLength() - nNameOffset);
+                }
                 aName.clear();
                 aNumbers.clear();
             }
@@ -1550,11 +1627,17 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
             {
                 // Remember key.
                 aName = pName->GetValue();
+                nNameOffset = pName->GetLocation();
             }
             else
             {
                 // Name-name key-value.
                 rDictionary[aName] = pName;
+                if (pThisObject)
+                {
+                    pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+                    pThisObject->SetDictionaryKeyValueLength(aName, pName->GetLocation() + pName->GetLength() - nNameOffset);
+                }
                 aName.clear();
             }
             continue;
@@ -1567,7 +1650,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
             continue;
         }
 
-        if (pArray && dynamic_cast<PDFEndArrayElement*>(rElements[i].get()))
+        auto pEndArr = dynamic_cast<PDFEndArrayElement*>(rElements[i].get());
+        if (pArray && pEndArr)
         {
             if (!aNumbers.empty())
             {
@@ -1576,6 +1660,12 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
                 aNumbers.clear();
             }
             rDictionary[aName] = pArray;
+            if (pThisObject)
+            {
+                pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
+                // Include the ending ']' in the length of the key - (array)value pair length.
+                pThisObject->SetDictionaryKeyValueLength(aName, pEndArr->GetOffset() - nNameOffset + 1);
+            }
             aName.clear();
             pArray = nullptr;
             continue;
@@ -1587,6 +1677,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
             if (!pArray)
             {
                 rDictionary[aName] = pReference;
+                if (pThisObject)
+                    pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
                 aName.clear();
             }
             else
@@ -1601,6 +1693,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
         if (pLiteralString)
         {
             rDictionary[aName] = pLiteralString;
+            if (pThisObject)
+                pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
             aName.clear();
             continue;
         }
@@ -1611,6 +1705,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
             if (!pArray)
             {
                 rDictionary[aName] = pHexString;
+                if (pThisObject)
+                    pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
                 aName.clear();
             }
             else
@@ -1633,6 +1729,8 @@ void PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement>
     if (!aNumbers.empty())
     {
         rDictionary[aName] = aNumbers.back();
+        if (pThisObject)
+            pThisObject->SetDictionaryKeyOffset(aName, nNameOffset);
         aName.clear();
         aNumbers.clear();
     }
@@ -1690,6 +1788,34 @@ sal_uInt64 PDFObjectElement::GetDictionaryOffset()
     return m_nDictionaryOffset;
 }
 
+void PDFObjectElement::SetDictionaryKeyOffset(const OString& rKey, sal_uInt64 nOffset)
+{
+    m_aDictionaryKeyOffset[rKey] = nOffset;
+}
+
+void PDFObjectElement::SetDictionaryKeyValueLength(const OString& rKey, sal_uInt64 nLength)
+{
+    m_aDictionaryKeyValueLength[rKey] = nLength;
+}
+
+sal_uInt64 PDFObjectElement::GetDictionaryKeyOffset(const OString& rKey) const
+{
+    auto it = m_aDictionaryKeyOffset.find(rKey);
+    if (it == m_aDictionaryKeyOffset.end())
+        return 0;
+
+    return it->second;
+}
+
+sal_uInt64 PDFObjectElement::GetDictionaryKeyValueLength(const OString& rKey) const
+{
+    auto it = m_aDictionaryKeyValueLength.find(rKey);
+    if (it == m_aDictionaryKeyValueLength.end())
+        return 0;
+
+    return it->second;
+}
+
 void PDFObjectElement::SetDictionaryLength(sal_uInt64 nDictionaryLength)
 {
     m_nDictionaryLength = nDictionaryLength;
@@ -1878,6 +2004,12 @@ bool PDFEndDictionaryElement::Read(SvStream& rStream)
     return true;
 }
 
+PDFNameElement::PDFNameElement()
+    : m_nLocation(0),
+      m_nLength(0)
+{
+}
+
 bool PDFNameElement::Read(SvStream& rStream)
 {
     char ch;
@@ -1887,6 +2019,7 @@ bool PDFNameElement::Read(SvStream& rStream)
         SAL_WARN("xmlsecurity.pdfio", "PDFNameElement::Read: unexpected character: " << ch);
         return false;
     }
+    m_nLocation = rStream.Tell();
 
     if (rStream.IsEof())
     {
@@ -1918,6 +2051,16 @@ const OString& PDFNameElement::GetValue() const
     return m_aValue;
 }
 
+sal_uInt64 PDFNameElement::GetLocation() const
+{
+    return m_nLocation;
+}
+
+sal_uInt64 PDFNameElement::GetLength() const
+{
+    return m_nLength;
+}
+
 PDFStreamElement::PDFStreamElement(size_t nLength)
     : m_nLength(nLength)
 {
@@ -1941,6 +2084,11 @@ bool PDFEndObjectElement::Read(SvStream& /*rStream*/)
     return true;
 }
 
+PDFArrayElement::PDFArrayElement()
+    : m_nOffset(0)
+{
+}
+
 bool PDFArrayElement::Read(SvStream& rStream)
 {
     char ch;
@@ -1950,6 +2098,7 @@ bool PDFArrayElement::Read(SvStream& rStream)
         SAL_WARN("xmlsecurity.pdfio", "PDFArrayElement::Read: unexpected character: " << ch);
         return false;
     }
+    m_nOffset = rStream.Tell();
 
     SAL_INFO("xmlsecurity.pdfio", "PDFArrayElement::Read: '['");
 
@@ -1966,8 +2115,14 @@ const std::vector<PDFElement*>& PDFArrayElement::GetElements()
     return m_aElements;
 }
 
+PDFEndArrayElement::PDFEndArrayElement()
+    : m_nOffset(0)
+{
+}
+
 bool PDFEndArrayElement::Read(SvStream& rStream)
 {
+    m_nOffset = rStream.Tell();
     char ch;
     rStream.ReadChar(ch);
     if (ch != ']')
@@ -1981,6 +2136,10 @@ bool PDFEndArrayElement::Read(SvStream& rStream)
     return true;
 }
 
+sal_uInt64 PDFEndArrayElement::GetOffset() const
+{
+    return m_nOffset;
+}
 
 } // namespace pdfio
 } // namespace xmlsecurity


More information about the Libreoffice-commits mailing list