summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.co.uk>2016-10-18 09:21:05 +0200
committerMiklos Vajna <vmiklos@collabora.co.uk>2016-10-18 10:58:57 +0200
commit9a3b752756204307a0439c4e3534c094c6ee979d (patch)
tree09fef1a408e811565df875c22211112df7c4b452
parentc486e875de7c8e845594f5043a37ee8800865782 (diff)
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
-rw-r--r--xmlsecurity/inc/pdfio/pdfdocument.hxx8
-rw-r--r--xmlsecurity/source/pdfio/pdfdocument.cxx117
-rw-r--r--xmlsecurity/source/pdfio/pdfverify.cxx62
3 files changed, 167 insertions, 20 deletions
diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx
index 0eaaa0954801..2f95b7dfce48 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 8cd2b5cdb05f..0ab997657640 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 30e085cf2913..5df1fffc90cf 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;