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

Miklos Vajna vmiklos at collabora.co.uk
Mon Oct 17 08:26:50 UTC 2016


 xmlsecurity/source/helper/pdfsignaturehelper.cxx |   17 +++++
 xmlsecurity/source/pdfio/pdfdocument.cxx         |   74 ++++++++++++++++-------
 2 files changed, 70 insertions(+), 21 deletions(-)

New commits:
commit d19cb7f5974216d3c52f758f00557a001dd1bd40
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Mon Oct 17 08:13:03 2016 +0200

    xmlsecurity: detect if PDF signature doesn't sign the whole file
    
    For ODF signatures we require that all streams of the storage are
    signed.  The PDF equivalent of this is to ensure that the byte range is
    the entire file, including the signature dictionary but excluding the
    signature value itself.
    
    Change-Id: Ie47f42913e2aa960f35079eb981768cd47fb9f92
    Reviewed-on: https://gerrit.libreoffice.org/29890
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>

diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 4711084..8cd2b5c 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -777,6 +777,54 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat
             rInformation.ouDescription = aBuffer.makeStringAndClear();
     }
 
+    // Build a list of offset-length pairs, representing the signed bytes.
+    std::vector<std::pair<size_t, size_t>> aByteRanges;
+    size_t nByteRangeOffset = 0;
+    const std::vector<PDFElement*>& rByteRangeElements = pByteRange->GetElements();
+    for (size_t i = 0; i < rByteRangeElements.size(); ++i)
+    {
+        auto pNumber = dynamic_cast<PDFNumberElement*>(rByteRangeElements[i]);
+        if (!pNumber)
+        {
+            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::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();
+        aByteRanges.push_back(std::make_pair(nByteRangeOffset, nByteRangeLength));
+    }
+
+    // Detect if the byte ranges don't cover everything, but the signature itself.
+    if (aByteRanges.size() < 2)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: expected 2 byte ranges");
+        return false;
+    }
+    if (aByteRanges[0].first != 0)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: first range start is not 0");
+        return false;
+    }
+    // 2 is the leading "<" and the trailing ">" around the hex string.
+    size_t nSignatureLength = pContents->GetValue().getLength() + 2;
+    if (aByteRanges[1].first != (aByteRanges[0].second + nSignatureLength))
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: second range start is not the end of the signature");
+        return false;
+    }
+    rStream.Seek(STREAM_SEEK_TO_END);
+    size_t nFileEnd = rStream.Tell();
+    if ((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;
+    }
+
     // At this point there is no obviously missing info to validate the
     // signature.
     std::vector<unsigned char> aSignature = PDFDocument::DecodeHexString(pContents);
@@ -837,37 +885,21 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat
     }
 
     // We have a hash, update it with the byte ranges.
-    size_t nByteRangeOffset = 0;
-    const std::vector<PDFElement*>& rByteRangeElements = pByteRange->GetElements();
-    for (size_t i = 0; i < rByteRangeElements.size(); ++i)
+    for (const auto& rByteRange : aByteRanges)
     {
-        auto pNumber = dynamic_cast<PDFNumberElement*>(rByteRangeElements[i]);
-        if (!pNumber)
-        {
-            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: signature offset and length has to be a number");
-            return false;
-        }
-
-        if (i % 2 == 0)
-        {
-            nByteRangeOffset = pNumber->GetValue();
-            continue;
-        }
-
-        rStream.Seek(nByteRangeOffset);
-        size_t nByteRangeLength = pNumber->GetValue();
+        rStream.Seek(rByteRange.first);
 
         // And now hash this byte range.
         const int nChunkLen = 4096;
         std::vector<unsigned char> aBuffer(nChunkLen);
-        for (size_t nByte = 0; nByte < nByteRangeLength;)
+        for (size_t nByte = 0; nByte < rByteRange.second;)
         {
-            size_t nRemainingSize = nByteRangeLength - nByte;
+            size_t nRemainingSize = rByteRange.second - nByte;
             if (nRemainingSize < nChunkLen)
             {
                 rStream.ReadBytes(aBuffer.data(), nRemainingSize);
                 HASH_Update(pHASHContext, aBuffer.data(), nRemainingSize);
-                nByte = nByteRangeLength;
+                nByte = rByteRange.second;
             }
             else
             {
commit d67a7ff3dfd726372d3619fe963a5b90f24a9ebd
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Mon Oct 17 08:12:17 2016 +0200

    xmlsecurity: verify certificate of PDF signatures
    
    We patch xmlsec to not verify certificates, and the PDF tokenizer in
    xmlsecurity doesn't do that, either. The point of doing so, is that the
    DocumentSignatureInformation UNO struct has separate CertificateStatus
    and SignatureIsValid fields for the validity of the certificate and the
    signature.
    
    That means the certificate has to be validated somewhere as well.
    ZIP-based formats do that in
    DocumentDigitalSignatures::ImplVerifySignatures(), and this commit
    implements the same for PDF signatures, too.
    
    Change-Id: Ic486afc8f392625b1efcad989fd9053b014a261b
    Reviewed-on: https://gerrit.libreoffice.org/29889
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>

diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
index cc4b388..2e6fa89 100644
--- a/xmlsecurity/source/helper/pdfsignaturehelper.cxx
+++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
@@ -11,6 +11,7 @@
 
 #include <memory>
 
+#include <com/sun/star/security/CertificateValidity.hpp>
 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
 
 #include <comphelper/sequence.hxx>
@@ -82,6 +83,22 @@ uno::Sequence<security::DocumentSignatureInformation> PDFSignatureHelper::GetDoc
         security::DocumentSignatureInformation& rExternal = aRet[i];
         rExternal.SignatureIsValid = rInternal.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
         rExternal.Signer = xSecurityEnvironment->createCertificateFromAscii(rInternal.ouX509Certificate);
+
+        // Verify certificate.
+        if (rExternal.Signer.is())
+        {
+            try
+            {
+                rExternal.CertificateStatus = xSecurityEnvironment->verifyCertificate(rExternal.Signer, {});
+            }
+            catch (const uno::SecurityException& rException)
+            {
+                SAL_WARN("xmlsecurity.helper", "failed to verify certificate: " << rException.Message);
+                rExternal.CertificateStatus = security::CertificateValidity::INVALID;
+            }
+        }
+        else
+            rExternal.CertificateStatus = security::CertificateValidity::INVALID;
     }
 
     return aRet;


More information about the Libreoffice-commits mailing list