nss: upgrade to release 3.73
[LibreOffice.git] / xmlsecurity / source / helper / pdfsignaturehelper.cxx
blobffadecfaea14a098f2468fea748baa9befad32fe
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <pdfsignaturehelper.hxx>
12 #include <memory>
14 #include <com/sun/star/io/XTruncate.hpp>
15 #include <com/sun/star/io/XStream.hpp>
16 #include <com/sun/star/security/CertificateValidity.hpp>
17 #include <com/sun/star/uno/SecurityException.hpp>
18 #include <com/sun/star/security/DocumentSignatureInformation.hpp>
19 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
20 #include <com/sun/star/drawing/XShapes.hpp>
21 #include <com/sun/star/frame/XModel.hpp>
22 #include <com/sun/star/frame/XStorable.hpp>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <com/sun/star/drawing/XDrawView.hpp>
26 #include <comphelper/propertysequence.hxx>
27 #include <sal/log.hxx>
28 #include <tools/diagnose_ex.h>
29 #include <unotools/mediadescriptor.hxx>
30 #include <unotools/streamwrap.hxx>
31 #include <unotools/ucbstreamhelper.hxx>
32 #include <vcl/filter/pdfdocument.hxx>
33 #include <vcl/checksum.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <svl/cryptosign.hxx>
36 #include <config_features.h>
37 #include <vcl/filter/PDFiumLibrary.hxx>
38 #if HAVE_FEATURE_PDFIUM
39 #include <fpdf_signature.h>
40 #endif
42 using namespace ::com::sun::star;
44 namespace
46 /// Gets the current page of the current view from xModel and puts it to the 1-based rPage.
47 bool GetSignatureLinePage(const uno::Reference<frame::XModel>& xModel, sal_Int32& rPage)
49 uno::Reference<drawing::XDrawView> xController(xModel->getCurrentController(), uno::UNO_QUERY);
50 if (!xController.is())
52 return false;
55 uno::Reference<beans::XPropertySet> xPage(xController->getCurrentPage(), uno::UNO_QUERY);
56 if (!xPage.is())
58 return false;
61 return xPage->getPropertyValue("Number") >>= rPage;
64 /// If the currently selected shape is a Draw signature line, export that to PDF.
65 void GetSignatureLineShape(const uno::Reference<frame::XModel>& xModel, sal_Int32& rPage,
66 std::vector<sal_Int8>& rSignatureLineShape)
68 if (!xModel.is())
70 return;
73 if (!GetSignatureLinePage(xModel, rPage))
75 return;
78 uno::Reference<drawing::XShapes> xShapes(xModel->getCurrentSelection(), uno::UNO_QUERY);
79 if (!xShapes.is() || xShapes->getCount() < 1)
81 return;
84 uno::Reference<beans::XPropertySet> xShapeProps(xShapes->getByIndex(0), uno::UNO_QUERY);
85 if (!xShapeProps.is())
87 return;
90 comphelper::SequenceAsHashMap aMap(xShapeProps->getPropertyValue("InteropGrabBag"));
91 auto it = aMap.find("SignatureCertificate");
92 if (it == aMap.end())
94 return;
97 // We know that we add a signature line shape to an existing PDF at this point.
99 uno::Reference<frame::XStorable> xStorable(xModel, uno::UNO_QUERY);
100 if (!xStorable.is())
102 return;
105 // Export just the signature line.
106 utl::MediaDescriptor aMediaDescriptor;
107 aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
108 SvMemoryStream aStream;
109 uno::Reference<io::XOutputStream> xStream(new utl::OStreamWrapper(aStream));
110 aMediaDescriptor["OutputStream"] <<= xStream;
111 uno::Sequence<beans::PropertyValue> aFilterData(
112 comphelper::InitPropertySequence({ { "Selection", uno::Any(xShapes) } }));
113 aMediaDescriptor["FilterData"] <<= aFilterData;
114 xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
115 xStream->flush();
117 aStream.Seek(0);
118 rSignatureLineShape = std::vector<sal_Int8>(aStream.GetSize());
119 aStream.ReadBytes(rSignatureLineShape.data(), rSignatureLineShape.size());
122 #if HAVE_FEATURE_PDFIUM
123 /// Represents a parsed signature.
124 struct Signature
126 std::unique_ptr<vcl::pdf::PDFiumSignature> m_pSignature;
127 /// Offset+length pairs.
128 std::vector<std::pair<size_t, size_t>> m_aByteRanges;
131 /// Turns an array of floats into offset + length pairs.
132 void GetByteRangesFromPDF(std::unique_ptr<vcl::pdf::PDFiumSignature>& pSignature,
133 std::vector<std::pair<size_t, size_t>>& rByteRanges)
135 int nByteRangeLen = FPDFSignatureObj_GetByteRange(pSignature->getPointer(), nullptr, 0);
136 if (nByteRangeLen <= 0)
138 SAL_WARN("xmlsecurity.helper", "GetByteRangesFromPDF: no byte ranges");
139 return;
142 std::vector<int> aByteRange(nByteRangeLen);
143 FPDFSignatureObj_GetByteRange(pSignature->getPointer(), aByteRange.data(), aByteRange.size());
145 size_t nByteRangeOffset = 0;
146 for (size_t i = 0; i < aByteRange.size(); ++i)
148 if (i % 2 == 0)
150 nByteRangeOffset = aByteRange[i];
151 continue;
154 size_t nLength = aByteRange[i];
155 rByteRanges.emplace_back(nByteRangeOffset, nLength);
159 /// Determines the last position that is covered by a signature.
160 bool GetEOFOfSignature(const Signature& rSignature, size_t& rEOF)
162 if (rSignature.m_aByteRanges.size() < 2)
164 return false;
167 rEOF = rSignature.m_aByteRanges[1].first + rSignature.m_aByteRanges[1].second;
168 return true;
172 * Get the value of the "modification detection and prevention" permission:
173 * Valid values are 1, 2 and 3: only 3 allows annotations after signing.
175 int GetMDPPerm(const std::vector<Signature>& rSignatures)
177 int nRet = 3;
179 if (rSignatures.empty())
181 return nRet;
184 for (const auto& rSignature : rSignatures)
186 int nPerm = FPDFSignatureObj_GetDocMDPPermission(rSignature.m_pSignature->getPointer());
187 if (nPerm != 0)
189 return nPerm;
193 return nRet;
196 /// Checks if there are unsigned incremental updates between the signatures or after the last one.
197 bool IsCompleteSignature(SvStream& rStream, const Signature& rSignature,
198 const std::set<unsigned int>& rSignedEOFs,
199 const std::vector<unsigned int>& rAllEOFs)
201 size_t nSignatureEOF = 0;
202 if (!GetEOFOfSignature(rSignature, nSignatureEOF))
204 return false;
207 bool bFoundOwn = false;
208 for (const auto& rEOF : rAllEOFs)
210 if (rEOF == nSignatureEOF)
212 bFoundOwn = true;
213 continue;
216 if (!bFoundOwn)
218 continue;
221 if (rSignedEOFs.find(rEOF) == rSignedEOFs.end())
223 // Unsigned incremental update found.
224 return false;
228 // Make sure we find the incremental update of the signature itself.
229 if (!bFoundOwn)
231 return false;
234 // No additional content after the last incremental update.
235 rStream.Seek(STREAM_SEEK_TO_END);
236 size_t nFileEnd = rStream.Tell();
237 return std::find(rAllEOFs.begin(), rAllEOFs.end(), nFileEnd) != rAllEOFs.end();
241 * Contains checksums of a PDF page, which is rendered without annotations. It also contains
242 * the geometry of a few dangerous annotation types.
244 struct PageChecksum
246 BitmapChecksum m_nPageContent;
247 std::vector<basegfx::B2DRectangle> m_aAnnotations;
248 bool operator==(const PageChecksum& rChecksum) const;
251 bool PageChecksum::operator==(const PageChecksum& rChecksum) const
253 if (m_nPageContent != rChecksum.m_nPageContent)
255 return false;
258 return m_aAnnotations == rChecksum.m_aAnnotations;
261 /// Collects the checksum of each page of one version of the PDF.
262 void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<PageChecksum>& rPageChecksums,
263 int nMDPPerm)
265 auto pPdfium = vcl::pdf::PDFiumLibrary::get();
266 std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
267 = pPdfium->openDocument(rStream.GetData(), rStream.GetSize());
268 if (!pPdfDocument)
270 return;
273 int nPageCount = pPdfDocument->getPageCount();
274 for (int nPage = 0; nPage < nPageCount; ++nPage)
276 std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(nPage);
277 if (!pPdfPage)
279 return;
282 PageChecksum aPageChecksum;
283 aPageChecksum.m_nPageContent = pPdfPage->getChecksum(nMDPPerm);
284 for (int i = 0; i < pPdfPage->getAnnotationCount(); ++i)
286 std::unique_ptr<vcl::pdf::PDFiumAnnotation> pPdfAnnotation = pPdfPage->getAnnotation(i);
287 vcl::pdf::PDFAnnotationSubType eType = pPdfAnnotation->getSubType();
288 switch (eType)
290 case vcl::pdf::PDFAnnotationSubType::Unknown:
291 case vcl::pdf::PDFAnnotationSubType::FreeText:
292 case vcl::pdf::PDFAnnotationSubType::Stamp:
293 case vcl::pdf::PDFAnnotationSubType::Redact:
294 aPageChecksum.m_aAnnotations.push_back(pPdfAnnotation->getRectangle());
295 break;
296 default:
297 break;
300 rPageChecksums.push_back(aPageChecksum);
305 * Checks if incremental updates after singing performed valid modifications only.
306 * nMDPPerm decides if annotations/commenting is OK, other changes are always not.
308 bool IsValidSignature(SvStream& rStream, const Signature& rSignature, int nMDPPerm)
310 size_t nSignatureEOF = 0;
311 if (!GetEOFOfSignature(rSignature, nSignatureEOF))
313 return false;
316 SvMemoryStream aSignatureStream;
317 sal_uInt64 nPos = rStream.Tell();
318 rStream.Seek(0);
319 aSignatureStream.WriteStream(rStream, nSignatureEOF);
320 rStream.Seek(nPos);
321 aSignatureStream.Seek(0);
322 std::vector<PageChecksum> aSignedPages;
323 AnalyizeSignatureStream(aSignatureStream, aSignedPages, nMDPPerm);
325 SvMemoryStream aFullStream;
326 nPos = rStream.Tell();
327 rStream.Seek(0);
328 aFullStream.WriteStream(rStream);
329 rStream.Seek(nPos);
330 aFullStream.Seek(0);
331 std::vector<PageChecksum> aAllPages;
332 AnalyizeSignatureStream(aFullStream, aAllPages, nMDPPerm);
334 // Fail if any page looks different after signing and at the end. Annotations/commenting doesn't
335 // count, though.
336 return aSignedPages == aAllPages;
340 * @param rInformation The actual result.
341 * @param rDocument the parsed document to see if the signature is partial.
342 * @return If we can determinate a result.
344 bool ValidateSignature(SvStream& rStream, const Signature& rSignature,
345 SignatureInformation& rInformation, int nMDPPerm,
346 const std::set<unsigned int>& rSignatureEOFs,
347 const std::vector<unsigned int>& rTrailerEnds)
349 int nContentsLen
350 = FPDFSignatureObj_GetContents(rSignature.m_pSignature->getPointer(), nullptr, 0);
351 if (nContentsLen <= 0)
353 SAL_WARN("xmlsecurity.helper", "ValidateSignature: no contents");
354 return false;
356 std::vector<unsigned char> aContents(nContentsLen);
357 FPDFSignatureObj_GetContents(rSignature.m_pSignature->getPointer(), aContents.data(),
358 aContents.size());
360 int nSubFilterLen
361 = FPDFSignatureObj_GetSubFilter(rSignature.m_pSignature->getPointer(), nullptr, 0);
362 std::vector<char> aSubFilterBuf(nSubFilterLen);
363 FPDFSignatureObj_GetSubFilter(rSignature.m_pSignature->getPointer(), aSubFilterBuf.data(),
364 aSubFilterBuf.size());
365 // Buffer is NUL-terminated.
366 OString aSubFilter(aSubFilterBuf.data(), aSubFilterBuf.size() - 1);
368 const bool bNonDetached = aSubFilter == "adbe.pkcs7.sha1";
369 if (aSubFilter.isEmpty()
370 || (aSubFilter != "adbe.pkcs7.detached" && !bNonDetached
371 && aSubFilter != "ETSI.CAdES.detached"))
373 if (aSubFilter.isEmpty())
374 SAL_WARN("xmlsecurity.helper", "ValidateSignature: missing sub-filter");
375 else
376 SAL_WARN("xmlsecurity.helper",
377 "ValidateSignature: unsupported sub-filter: '" << aSubFilter << "'");
378 return false;
381 // Reason / comment / description is optional.
382 int nReasonLen = FPDFSignatureObj_GetReason(rSignature.m_pSignature->getPointer(), nullptr, 0);
383 if (nReasonLen > 0)
385 std::vector<char16_t> aReasonBuf(nReasonLen);
386 FPDFSignatureObj_GetReason(rSignature.m_pSignature->getPointer(), aReasonBuf.data(),
387 aReasonBuf.size());
388 rInformation.ouDescription = OUString(aReasonBuf.data(), aReasonBuf.size() - 1);
391 // Date: used only when the time of signing is not available in the
392 // signature.
393 int nTimeLen = FPDFSignatureObj_GetTime(rSignature.m_pSignature->getPointer(), nullptr, 0);
394 if (nTimeLen > 0)
396 // Example: "D:20161027100104".
397 std::vector<char> aTimeBuf(nTimeLen);
398 FPDFSignatureObj_GetTime(rSignature.m_pSignature->getPointer(), aTimeBuf.data(),
399 aTimeBuf.size());
400 OString aM(aTimeBuf.data(), aTimeBuf.size() - 1);
401 if (aM.startsWith("D:") && aM.getLength() >= 16)
403 rInformation.stDateTime.Year = aM.copy(2, 4).toInt32();
404 rInformation.stDateTime.Month = aM.copy(6, 2).toInt32();
405 rInformation.stDateTime.Day = aM.copy(8, 2).toInt32();
406 rInformation.stDateTime.Hours = aM.copy(10, 2).toInt32();
407 rInformation.stDateTime.Minutes = aM.copy(12, 2).toInt32();
408 rInformation.stDateTime.Seconds = aM.copy(14, 2).toInt32();
412 // Detect if the byte ranges don't cover everything, but the signature itself.
413 if (rSignature.m_aByteRanges.size() < 2)
415 SAL_WARN("xmlsecurity.helper", "ValidateSignature: expected 2 byte ranges");
416 return false;
418 if (rSignature.m_aByteRanges[0].first != 0)
420 SAL_WARN("xmlsecurity.helper", "ValidateSignature: first range start is not 0");
421 return false;
423 // Binary vs hex dump and 2 is the leading "<" and the trailing ">" around the hex string.
424 size_t nSignatureLength = aContents.size() * 2 + 2;
425 if (rSignature.m_aByteRanges[1].first
426 != (rSignature.m_aByteRanges[0].second + nSignatureLength))
428 SAL_WARN("xmlsecurity.helper",
429 "ValidateSignature: second range start is not the end of the signature");
430 return false;
432 rInformation.bPartialDocumentSignature
433 = !IsCompleteSignature(rStream, rSignature, rSignatureEOFs, rTrailerEnds);
434 if (!IsValidSignature(rStream, rSignature, nMDPPerm))
436 SAL_WARN("xmlsecurity.helper", "ValidateSignature: invalid incremental update detected");
437 return false;
440 // At this point there is no obviously missing info to validate the
441 // signature.
442 return svl::crypto::Signing::Verify(rStream, rSignature.m_aByteRanges, bNonDetached, aContents,
443 rInformation);
445 #endif
448 PDFSignatureHelper::PDFSignatureHelper() = default;
450 bool PDFSignatureHelper::ReadAndVerifySignature(
451 const uno::Reference<io::XInputStream>& xInputStream)
453 if (!xInputStream.is())
455 SAL_WARN("xmlsecurity.helper", "input stream missing");
456 return false;
459 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
460 return ReadAndVerifySignatureSvStream(*pStream);
463 bool PDFSignatureHelper::ReadAndVerifySignatureSvStream(SvStream& rStream)
465 #if HAVE_FEATURE_PDFIUM
466 auto pPdfium = vcl::pdf::PDFiumLibrary::get();
467 SvMemoryStream aStream;
468 sal_uInt64 nPos = rStream.Tell();
469 rStream.Seek(0);
470 aStream.WriteStream(rStream);
471 rStream.Seek(nPos);
472 std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
473 = pPdfium->openDocument(aStream.GetData(), aStream.GetSize());
474 if (!pPdfDocument)
476 SAL_WARN("xmlsecurity.helper", "failed to read the document");
477 return false;
480 int nSignatureCount = pPdfDocument->getSignatureCount();
481 if (nSignatureCount <= 0)
483 return true;
485 std::vector<Signature> aSignatures(nSignatureCount);
486 for (int i = 0; i < nSignatureCount; ++i)
488 std::unique_ptr<vcl::pdf::PDFiumSignature> pSignature = pPdfDocument->getSignature(i);
489 std::vector<std::pair<size_t, size_t>> aByteRanges;
490 GetByteRangesFromPDF(pSignature, aByteRanges);
491 aSignatures[i] = Signature{ std::move(pSignature), aByteRanges };
494 std::set<unsigned int> aSignatureEOFs;
495 for (const auto& rSignature : aSignatures)
497 size_t nEOF = 0;
498 if (GetEOFOfSignature(rSignature, nEOF))
500 aSignatureEOFs.insert(nEOF);
504 std::vector<unsigned int> aTrailerEnds = pPdfDocument->getTrailerEnds();
506 m_aSignatureInfos.clear();
508 int nMDPPerm = GetMDPPerm(aSignatures);
510 for (size_t i = 0; i < aSignatures.size(); ++i)
512 SignatureInformation aInfo(i);
514 if (!ValidateSignature(rStream, aSignatures[i], aInfo, nMDPPerm, aSignatureEOFs,
515 aTrailerEnds))
517 SAL_WARN("xmlsecurity.helper", "failed to determine digest match");
520 m_aSignatureInfos.push_back(aInfo);
522 #else
523 (void)rStream;
524 #endif
526 return true;
529 SignatureInformations const& PDFSignatureHelper::GetSignatureInformations() const
531 return m_aSignatureInfos;
534 uno::Sequence<security::DocumentSignatureInformation>
535 PDFSignatureHelper::GetDocumentSignatureInformations(
536 const uno::Reference<xml::crypto::XSecurityEnvironment>& xSecEnv) const
538 uno::Sequence<security::DocumentSignatureInformation> aRet(m_aSignatureInfos.size());
540 for (size_t i = 0; i < m_aSignatureInfos.size(); ++i)
542 const SignatureInformation& rInternal = m_aSignatureInfos[i];
543 security::DocumentSignatureInformation& rExternal = aRet[i];
544 rExternal.SignatureIsValid
545 = rInternal.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
546 if (rInternal.GetSigningCertificate()
547 && !rInternal.GetSigningCertificate()->X509Certificate.isEmpty())
549 rExternal.Signer = xSecEnv->createCertificateFromAscii(
550 rInternal.GetSigningCertificate()->X509Certificate);
552 rExternal.PartialDocumentSignature = rInternal.bPartialDocumentSignature;
554 // Verify certificate.
555 if (rExternal.Signer.is())
559 rExternal.CertificateStatus = xSecEnv->verifyCertificate(rExternal.Signer, {});
561 catch (const uno::SecurityException&)
563 DBG_UNHANDLED_EXCEPTION("xmlsecurity.helper", "failed to verify certificate");
564 rExternal.CertificateStatus = security::CertificateValidity::INVALID;
567 else
568 rExternal.CertificateStatus = security::CertificateValidity::INVALID;
571 return aRet;
574 sal_Int32 PDFSignatureHelper::GetNewSecurityId() const { return m_aSignatureInfos.size(); }
576 void PDFSignatureHelper::SetX509Certificate(
577 const uno::Reference<security::XCertificate>& xCertificate)
579 m_xCertificate = xCertificate;
582 void PDFSignatureHelper::SetDescription(const OUString& rDescription)
584 m_aDescription = rDescription;
587 bool PDFSignatureHelper::Sign(const uno::Reference<frame::XModel>& xModel,
588 const uno::Reference<io::XInputStream>& xInputStream, bool bAdES)
590 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
591 vcl::filter::PDFDocument aDocument;
592 if (!aDocument.Read(*pStream))
594 SAL_WARN("xmlsecurity.helper", "failed to read the document");
595 return false;
598 sal_Int32 nPage = 0;
599 std::vector<sal_Int8> aSignatureLineShape;
600 GetSignatureLineShape(xModel, nPage, aSignatureLineShape);
601 if (nPage > 0)
603 // UNO page number is 1-based.
604 aDocument.SetSignaturePage(nPage - 1);
606 if (!aSignatureLineShape.empty())
608 aDocument.SetSignatureLine(aSignatureLineShape);
611 if (!aDocument.Sign(m_xCertificate, m_aDescription, bAdES))
613 SAL_WARN("xmlsecurity.helper", "failed to sign");
614 return false;
617 uno::Reference<io::XStream> xStream(xInputStream, uno::UNO_QUERY);
618 std::unique_ptr<SvStream> pOutStream(utl::UcbStreamHelper::CreateStream(xStream, true));
619 if (!aDocument.Write(*pOutStream))
621 SAL_WARN("xmlsecurity.helper", "failed to write signed data");
622 return false;
625 return true;
628 bool PDFSignatureHelper::RemoveSignature(const uno::Reference<io::XInputStream>& xInputStream,
629 sal_uInt16 nPosition)
631 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
632 vcl::filter::PDFDocument aDocument;
633 if (!aDocument.Read(*pStream))
635 SAL_WARN("xmlsecurity.helper", "failed to read the document");
636 return false;
639 if (!aDocument.RemoveSignature(nPosition))
641 SAL_WARN("xmlsecurity.helper", "failed to remove signature");
642 return false;
645 uno::Reference<io::XStream> xStream(xInputStream, uno::UNO_QUERY);
646 uno::Reference<io::XTruncate> xTruncate(xStream, uno::UNO_QUERY);
647 if (!xTruncate.is())
649 SAL_WARN("xmlsecurity.helper", "failed to truncate");
650 return false;
652 xTruncate->truncate();
653 std::unique_ptr<SvStream> pOutStream(utl::UcbStreamHelper::CreateStream(xStream, true));
654 if (!aDocument.Write(*pOutStream))
656 SAL_WARN("xmlsecurity.helper", "failed to write without signature");
657 return false;
660 return true;
663 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */