diff options
-rw-r--r-- | include/vcl/filter/PDFiumLibrary.hxx | 2 | ||||
-rw-r--r-- | xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p3-stamp.pdf | bin | 0 -> 22023 bytes | |||
-rw-r--r-- | xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx | 16 | ||||
-rw-r--r-- | xmlsecurity/source/pdfio/pdfdocument.cxx | 64 |
4 files changed, 77 insertions, 5 deletions
diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx index 3e2851d21e0e..2d93710ec5f8 100644 --- a/include/vcl/filter/PDFiumLibrary.hxx +++ b/include/vcl/filter/PDFiumLibrary.hxx @@ -57,6 +57,8 @@ public: FPDF_ClosePage(mpPage); } + FPDF_PAGE getPointer() { return mpPage; } + /// Get bitmap checksum of the page, without annotations/commenting. BitmapChecksum getChecksum(int nMDPPerm); }; diff --git a/xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p3-stamp.pdf b/xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p3-stamp.pdf Binary files differnew file mode 100644 index 000000000000..b30f5b03867c --- /dev/null +++ b/xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p3-stamp.pdf diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx index aaca2c5acc63..20241f016d52 100644 --- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx +++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx @@ -427,6 +427,22 @@ CPPUNIT_TEST_FIXTURE(PDFSigningTest, testBadCertP1) rInformation.nStatus); } +CPPUNIT_TEST_FIXTURE(PDFSigningTest, testBadCertP3Stamp) +{ + std::vector<SignatureInformation> aInfos + = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "bad-cert-p3-stamp.pdf", 1, + /*rExpectedSubFilter=*/OString()); + CPPUNIT_ASSERT(!aInfos.empty()); + SignatureInformation& rInformation = aInfos[0]; + + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 0 (SecurityOperationStatus_UNKNOWN) + // - Actual : 1 (SecurityOperationStatus_OPERATION_SUCCEEDED) + // i.e. adding a stamp annotation was not considered as a bad modification. + CPPUNIT_ASSERT_EQUAL(xml::crypto::SecurityOperationStatus::SecurityOperationStatus_UNKNOWN, + rInformation.nStatus); +} + /// Test writing a PAdES signature. CPPUNIT_TEST_FIXTURE(PDFSigningTest, testSigningCertificateAttribute) { diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx index 5580fed9d178..d154fa3dc6c5 100644 --- a/xmlsecurity/source/pdfio/pdfdocument.cxx +++ b/xmlsecurity/source/pdfio/pdfdocument.cxx @@ -24,6 +24,11 @@ #include <svl/cryptosign.hxx> #include <vcl/filter/pdfdocument.hxx> #include <vcl/bitmap.hxx> +#include <basegfx/range/b2drectangle.hxx> + +#if HAVE_FEATURE_PDFIUM +#include <fpdf_annot.h> +#endif using namespace com::sun::star; @@ -139,8 +144,30 @@ bool IsCompleteSignature(SvStream& rStream, vcl::filter::PDFDocument& rDocument, } #if HAVE_FEATURE_PDFIUM + +/** + * Contains checksums of a PDF page, which is rendered without annotations. It also contains + * the geometry of a few dangerous annotation types. + */ +struct PageChecksum +{ + BitmapChecksum m_nPageContent; + std::vector<basegfx::B2DRectangle> m_aAnnotations; + bool operator==(const PageChecksum& rChecksum) const; +}; + +bool PageChecksum::operator==(const PageChecksum& rChecksum) const +{ + if (m_nPageContent != rChecksum.m_nPageContent) + { + return false; + } + + return m_aAnnotations == rChecksum.m_aAnnotations; +} + /// Collects the checksum of each page of one version of the PDF. -void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<BitmapChecksum>& rPageChecksums, +void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<PageChecksum>& rPageChecksums, int nMDPPerm) { auto pPdfium = vcl::pdf::PDFiumLibrary::get(); @@ -156,8 +183,35 @@ void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<BitmapChecksum return; } - BitmapChecksum nPageChecksum = pPdfPage->getChecksum(nMDPPerm); - rPageChecksums.push_back(nPageChecksum); + PageChecksum aPageChecksum; + aPageChecksum.m_nPageContent = pPdfPage->getChecksum(nMDPPerm); + for (int i = 0; i < FPDFPage_GetAnnotCount(pPdfPage->getPointer()); ++i) + { + FPDF_ANNOTATION pAnnotation = FPDFPage_GetAnnot(pPdfPage->getPointer(), i); + int nType = FPDFAnnot_GetSubtype(pAnnotation); + switch (nType) + { + case FPDF_ANNOT_UNKNOWN: + case FPDF_ANNOT_FREETEXT: + case FPDF_ANNOT_STAMP: + case FPDF_ANNOT_REDACT: + { + basegfx::B2DRectangle aB2DRectangle; + FS_RECTF aRect; + if (FPDFAnnot_GetRect(pAnnotation, &aRect)) + { + aB2DRectangle = basegfx::B2DRectangle(aRect.left, aRect.top, aRect.right, + aRect.bottom); + } + aPageChecksum.m_aAnnotations.push_back(aB2DRectangle); + break; + } + default: + break; + } + FPDFPage_CloseAnnot(pAnnotation); + } + rPageChecksums.push_back(aPageChecksum); } } #endif @@ -181,7 +235,7 @@ bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignatu aSignatureStream.WriteStream(rStream, nSignatureEOF); rStream.Seek(nPos); aSignatureStream.Seek(0); - std::vector<BitmapChecksum> aSignedPages; + std::vector<PageChecksum> aSignedPages; AnalyizeSignatureStream(aSignatureStream, aSignedPages, nMDPPerm); SvMemoryStream aFullStream; @@ -190,7 +244,7 @@ bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignatu aFullStream.WriteStream(rStream); rStream.Seek(nPos); aFullStream.Seek(0); - std::vector<BitmapChecksum> aAllPages; + std::vector<PageChecksum> aAllPages; AnalyizeSignatureStream(aFullStream, aAllPages, nMDPPerm); // Fail if any page looks different after signing and at the end. Annotations/commenting doesn't |