[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