xmlsecurity/inc/pdfio/pdfdocument.hxx | 66 ++++++++++++++++++++++++ xmlsecurity/inc/sigstruct.hxx | 3 + xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx | 28 +++++++--- xmlsecurity/source/pdfio/pdfdocument.cxx | 70 +------------------------- 4 files changed, 93 insertions(+), 74 deletions(-)
New commits: commit 5cb580144c286117db485e605c79ce1139cb94fb Author: Miklos Vajna <vmik...@collabora.co.uk> Date: Thu Nov 24 17:37:09 2016 +0100 CppunitTest_xmlsecurity_pdfsigning: add PAdES testcase Assert the two user-visible changes: SHA-256 hashes and the SubFilter of the signature. Change-Id: I12a2355e2ddfc368bed4430a7b5ad244b5778afe Reviewed-on: https://gerrit.libreoffice.org/31173 Tested-by: Jenkins <c...@libreoffice.org> Reviewed-by: Miklos Vajna <vmik...@collabora.co.uk> diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx index 31a0546..e2f2913 100644 --- a/xmlsecurity/inc/pdfio/pdfdocument.hxx +++ b/xmlsecurity/inc/pdfio/pdfdocument.hxx @@ -27,9 +27,12 @@ namespace pdfio { class PDFTrailerElement; -class PDFObjectElement; class PDFHexStringElement; class PDFReferenceElement; +class PDFDocument; +class PDFDictionaryElement; +class PDFArrayElement; +class PDFStreamElement; /// A byte range in a PDF file. class PDFElement @@ -39,6 +42,67 @@ public: virtual ~PDFElement() { } }; +/// Indirect object: something with a unique ID. +class XMLSECURITY_DLLPUBLIC PDFObjectElement : public PDFElement +{ + PDFDocument& m_rDoc; + double m_fObjectValue; + double m_fGenerationValue; + std::map<OString, PDFElement*> m_aDictionary; + /// Position after the '<<' token. + sal_uInt64 m_nDictionaryOffset; + /// Length of the dictionary buffer till (before) the '<<' token. + sal_uInt64 m_nDictionaryLength; + PDFDictionaryElement* m_pDictionaryElement; + /// The contained direct array, if any. + PDFArrayElement* m_pArrayElement; + /// The stream of this object, used when this is an object stream. + PDFStreamElement* m_pStreamElement; + /// Objects of an object stream. + std::vector< std::unique_ptr<PDFObjectElement> > m_aStoredElements; + /// Elements of an object in an object stream. + std::vector< std::unique_ptr<PDFElement> > m_aElements; + /// Uncompressed buffer of an object in an object stream. + std::unique_ptr<SvMemoryStream> m_pStreamBuffer; + +public: + PDFObjectElement(PDFDocument& rDoc, double fObjectValue, double fGenerationValue); + bool Read(SvStream& rStream) override; + PDFElement* Lookup(const OString& rDictionaryKey); + PDFObjectElement* LookupObject(const OString& rDictionaryKey); + double GetObjectValue() const; + void SetDictionaryOffset(sal_uInt64 nDictionaryOffset); + sal_uInt64 GetDictionaryOffset(); + void SetDictionaryLength(sal_uInt64 nDictionaryLength); + sal_uInt64 GetDictionaryLength(); + PDFDictionaryElement* GetDictionary() const; + void SetDictionary(PDFDictionaryElement* pDictionaryElement); + void SetArray(PDFArrayElement* pArrayElement); + void SetStream(PDFStreamElement* pStreamElement); + PDFArrayElement* GetArray() const; + /// Parse objects stored in this object stream. + void ParseStoredObjects(); + std::vector< std::unique_ptr<PDFElement> >& GetStoredElements(); + SvMemoryStream* GetStreamBuffer() const; + void SetStreamBuffer(std::unique_ptr<SvMemoryStream>& pStreamBuffer); +}; + +/// Name object: a key string. +class XMLSECURITY_DLLPUBLIC PDFNameElement : public PDFElement +{ + OString m_aValue; + /// Offset after the '/' token. + sal_uInt64 m_nLocation; + /// Length till the next token start. + sal_uInt64 m_nLength; +public: + PDFNameElement(); + bool Read(SvStream& rStream) override; + const OString& GetValue() const; + sal_uInt64 GetLocation() const; + sal_uInt64 GetLength() const; +}; + enum class TokenizeMode { /// Full file. diff --git a/xmlsecurity/inc/sigstruct.hxx b/xmlsecurity/inc/sigstruct.hxx index 6dd4f7f..ab455d5 100644 --- a/xmlsecurity/inc/sigstruct.hxx +++ b/xmlsecurity/inc/sigstruct.hxx @@ -102,11 +102,14 @@ struct SignatureInformation OUString ouCertDigest; /// A full OOXML signguature for unchanged roundtrip, empty for ODF. css::uno::Sequence<sal_Int8> aSignatureBytes; + /// For PDF: digest format, from css::xml::crypto::DigestID + sal_Int32 nDigestID; SignatureInformation( sal_Int32 nId ) { nSecurityId = nId; nStatus = css::xml::crypto::SecurityOperationStatus_UNKNOWN; + nDigestID = 0; } }; diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx index 99e176b..4d0ce52 100644 --- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx +++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx @@ -40,7 +40,7 @@ class PDFSigningTest : public test::BootstrapFixture * Read a pdf and make sure that it has the expected number of valid * signatures. */ - std::vector<SignatureInformation> verify(const OUString& rURL, size_t nCount); + std::vector<SignatureInformation> verify(const OUString& rURL, size_t nCount, const OString& rExpectedSubFilter); public: PDFSigningTest(); @@ -98,7 +98,7 @@ void PDFSigningTest::setUp() #endif } -std::vector<SignatureInformation> PDFSigningTest::verify(const OUString& rURL, size_t nCount) +std::vector<SignatureInformation> PDFSigningTest::verify(const OUString& rURL, size_t nCount, const OString& rExpectedSubFilter) { uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(mxComponentContext); uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString()); @@ -115,6 +115,15 @@ std::vector<SignatureInformation> PDFSigningTest::verify(const OUString& rURL, s bool bLast = i == aSignatures.size() - 1; CPPUNIT_ASSERT(xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[i], aInfo, bLast)); aRet.push_back(aInfo); + + if (!rExpectedSubFilter.isEmpty()) + { + xmlsecurity::pdfio::PDFObjectElement* pValue = aSignatures[i]->LookupObject("V"); + CPPUNIT_ASSERT(pValue); + auto pSubFilter = dynamic_cast<xmlsecurity::pdfio::PDFNameElement*>(pValue->Lookup("SubFilter")); + CPPUNIT_ASSERT(pSubFilter); + CPPUNIT_ASSERT_EQUAL(rExpectedSubFilter, pSubFilter->GetValue()); + } } return aRet; @@ -148,7 +157,7 @@ bool PDFSigningTest::sign(const OUString& rInURL, const OUString& rOutURL, size_ } // This was nOriginalSignatureCount when PDFDocument::Sign() silently returned success, without doing anything. - verify(rOutURL, nOriginalSignatureCount + 1); + verify(rOutURL, nOriginalSignatureCount + 1, /*rExpectedSubFilter=*/OString()); return true; } @@ -163,11 +172,14 @@ void PDFSigningTest::testPDFAdd() if (bHadCertificates) { + // Assert that the SubFilter is not adbe.pkcs7.detached in the bAdES case. + std::vector<SignatureInformation> aInfos = verify(aOutURL, 1, "ETSI.CAdES.detached"); // Make sure the timestamp is correct. - std::vector<SignatureInformation> aInfos = verify(aOutURL, 1); DateTime aDateTime(DateTime::SYSTEM); // This was 0 (on Windows), as neither the /M key nor the PKCS#7 blob contained a timestamp. CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(aDateTime.GetYear()), aInfos[0].stDateTime.Year); + // Assert that the digest algorithm is not SHA-1 in the bAdES case. + CPPUNIT_ASSERT_EQUAL(xml::crypto::DigestID::SHA256, aInfos[0].nDigestID); } } @@ -218,7 +230,7 @@ void PDFSigningTest::testPDFRemove() // Read back the pdf and make sure that it no longer has signatures. // This failed when PDFDocument::RemoveSignature() silently returned // success, without doing anything. - verify(aOutURL, 0); + verify(aOutURL, 0, /*rExpectedSubFilter=*/OString()); } void PDFSigningTest::testPDFRemoveAll() @@ -259,7 +271,7 @@ void PDFSigningTest::testPDF14Adobe() // Two signatures, first is SHA1, the second is SHA256. // This was 0, as we failed to find the Annots key's value when it was a // reference-to-array, not an array. - std::vector<SignatureInformation> aInfos = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14adobe.pdf", 2); + std::vector<SignatureInformation> aInfos = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14adobe.pdf", 2, /*rExpectedSubFilter=*/OString()); // This was 0, out-of-PKCS#7 signature date wasn't read. CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(2016), aInfos[1].stDateTime.Year); } @@ -270,7 +282,7 @@ void PDFSigningTest::testPDF16Adobe() // stream with a predictor. And a valid signature. // Found signatures was 0, as parsing failed due to lack of support for // these features. - verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf16adobe.pdf", 1); + verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf16adobe.pdf", 1, /*rExpectedSubFilter=*/OString()); } void PDFSigningTest::testPDF16Add() @@ -299,7 +311,7 @@ void PDFSigningTest::testPDF14LOWin() // algorithm when it meant SEC_OID_SHA1, make sure we tolerate that on all // platforms. // This failed, as NSS HASH_Create() didn't handle the sign algorithm. - verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14lowin.pdf", 1); + verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14lowin.pdf", 1, /*rExpectedSubFilter=*/OString()); } CPPUNIT_TEST_SUITE_REGISTRATION(PDFSigningTest); diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx index 1537500..29b4a02 100644 --- a/xmlsecurity/source/pdfio/pdfdocument.cxx +++ b/xmlsecurity/source/pdfio/pdfdocument.cxx @@ -54,7 +54,6 @@ namespace pdfio const int MAX_SIGNATURE_CONTENT_LENGTH = 50000; class PDFTrailerElement; -class PDFObjectElement; /// A one-liner comment. class PDFCommentElement : public PDFElement @@ -85,54 +84,6 @@ public: }; class PDFReferenceElement; -class PDFDictionaryElement; -class PDFArrayElement; -class PDFStreamElement; - -/// Indirect object: something with a unique ID. -class PDFObjectElement : public PDFElement -{ - PDFDocument& m_rDoc; - double m_fObjectValue; - double m_fGenerationValue; - std::map<OString, PDFElement*> m_aDictionary; - /// Position after the '<<' token. - sal_uInt64 m_nDictionaryOffset; - /// Length of the dictionary buffer till (before) the '<<' token. - sal_uInt64 m_nDictionaryLength; - PDFDictionaryElement* m_pDictionaryElement; - /// The contained direct array, if any. - PDFArrayElement* m_pArrayElement; - /// The stream of this object, used when this is an object stream. - PDFStreamElement* m_pStreamElement; - /// Objects of an object stream. - std::vector< std::unique_ptr<PDFObjectElement> > m_aStoredElements; - /// Elements of an object in an object stream. - std::vector< std::unique_ptr<PDFElement> > m_aElements; - /// Uncompressed buffer of an object in an object stream. - std::unique_ptr<SvMemoryStream> m_pStreamBuffer; - -public: - PDFObjectElement(PDFDocument& rDoc, double fObjectValue, double fGenerationValue); - bool Read(SvStream& rStream) override; - PDFElement* Lookup(const OString& rDictionaryKey); - PDFObjectElement* LookupObject(const OString& rDictionaryKey); - double GetObjectValue() const; - void SetDictionaryOffset(sal_uInt64 nDictionaryOffset); - sal_uInt64 GetDictionaryOffset(); - void SetDictionaryLength(sal_uInt64 nDictionaryLength); - sal_uInt64 GetDictionaryLength(); - PDFDictionaryElement* GetDictionary() const; - void SetDictionary(PDFDictionaryElement* pDictionaryElement); - void SetArray(PDFArrayElement* pArrayElement); - void SetStream(PDFStreamElement* pStreamElement); - PDFArrayElement* GetArray() const; - /// Parse objects stored in this object stream. - void ParseStoredObjects(); - std::vector< std::unique_ptr<PDFElement> >& GetStoredElements(); - SvMemoryStream* GetStreamBuffer() const; - void SetStreamBuffer(std::unique_ptr<SvMemoryStream>& pStreamBuffer); -}; /// Dictionary object: a set key-value pairs. class PDFDictionaryElement : public PDFElement @@ -170,22 +121,6 @@ public: sal_uInt64 GetLocation() const; }; -/// Name object: a key string. -class PDFNameElement : public PDFElement -{ - OString m_aValue; - /// Offset after the '/' token. - sal_uInt64 m_nLocation; - /// Length till the next token start. - sal_uInt64 m_nLength; -public: - PDFNameElement(); - bool Read(SvStream& rStream) override; - const OString& GetValue() const; - sal_uInt64 GetLocation() const; - sal_uInt64 GetLength() const; -}; - /// Reference object: something with a unique ID. class PDFReferenceElement : public PDFElement { @@ -2239,9 +2174,14 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat { case SEC_OID_SHA1: nMaxResultLen = msfilter::SHA1_HASH_LENGTH; + rInformation.nDigestID = xml::crypto::DigestID::SHA1; break; case SEC_OID_SHA256: nMaxResultLen = msfilter::SHA256_HASH_LENGTH; + rInformation.nDigestID = xml::crypto::DigestID::SHA256; + break; + case SEC_OID_SHA512: + nMaxResultLen = msfilter::SHA512_HASH_LENGTH; break; default: SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: unrecognized algorithm"); _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits