[Libreoffice-commits] core.git: Branch 'libreoffice-5-3' - vcl/source xmlsecurity/CppunitTest_xmlsecurity_signing.mk xmlsecurity/inc xmlsecurity/qa xmlsecurity/source

Miklos Vajna vmiklos at collabora.co.uk
Tue Nov 29 08:09:19 UTC 2016


 vcl/source/gdi/pdfwriter_impl.cxx                |  117 +++++++++++++++++++++++
 xmlsecurity/CppunitTest_xmlsecurity_signing.mk   |    1 
 xmlsecurity/inc/pdfio/pdfdocument.hxx            |   66 ++++++++++++
 xmlsecurity/inc/sigstruct.hxx                    |    3 
 xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx    |   28 +++--
 xmlsecurity/qa/unit/signing/data/certificate.crt |   27 -----
 xmlsecurity/qa/unit/signing/data/key3.db         |binary
 xmlsecurity/qa/unit/signing/signing.cxx          |   94 ++++++++++++++----
 xmlsecurity/source/pdfio/pdfdocument.cxx         |   74 --------------
 9 files changed, 286 insertions(+), 124 deletions(-)

New commits:
commit d02c3ba8c5e723561edb694e7ed8b2f2c33604af
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Wed Nov 23 11:27:32 2016 +0100

    vcl mscrypto PDF sign: bring it up to date with NSS, part 1
    
    This is a combination of 6 commits:
    
    1) vcl mscrypto PDF sign: add initial 'signing-certificate' signed attribute
    
    Equivalent of the earlier NSS commit, payload is just an empty sequence
    at the moment.
    
    (cherry picked from commit cb851cbb09adc637bb6e8095050292f7a8c6a7b1)
    
    2) vcl mscrypto PDF sign: write ESSCertIDv2
    
    With this, the value of signing-certificate conforms to the RFC on
    Windows as well.
    
    (cherry picked from commit b12410f212658996fdb5fb291a06038e9ac39b2e)
    
    3) xmlsecurity mscrypto PDF sign: conditionally add back CAdES SubFilter
    
    We can now write that on Windows as well when requested, after the
    signing-certificate attribute is implemented using mscrypto.
    
    With this, the PAdES validator at
    <http://signatures-conformance-checker.etsi.org/protected/upload.php?sigtype=padesconf>
    finds our Windows signature valid.
    
    (cherry picked from commit 8a279d7de4cf94c99f655f6edd0da0c24ab4003c)
    
    4) CppunitTest_xmlsecurity_signing: don't assume we always have a signing cert
    
    This makes this suite in sync with CppunitTest_xmlsecurity_pdfsigning. A
    signing certificate is available on 64bit NSS platforms, as there we
    provide a pre-created NSS db, but on other platforms by default there is
    just no signing certificate.  The certificate.crt I added earlier is not
    enough, that's just the certificate, but it doesn't provide a private
    key.
    
    (cherry picked from commit 748f778d0f42f2cbb78a7ca7e013bfbd77cdf2b7)
    
    5) CppunitTest_xmlsecurity_signing: add XAdES testcase
    
    Assert the two user-visible changes: SHA-256 hashes and the digest of
    the signing certificate.
    
    (cherry picked from commit 426495cb441e6a83cd0d1f74b0ddf656322815b5)
    
    6) CppunitTest_xmlsecurity_pdfsigning: add PAdES testcase
    
    Assert the two user-visible changes: SHA-256 hashes and the SubFilter of the
    signature.
    
    (cherry picked from commit 5cb580144c286117db485e605c79ce1139cb94fb)
    
    Change-Id: I12a2355e2ddfc368bed4430a7b5ad244b5778afe
    Reviewed-on: https://gerrit.libreoffice.org/31316
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Tested-by: Miklos Vajna <vmiklos at collabora.co.uk>

diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index e5af4d4..372f08e 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -6805,6 +6805,101 @@ typedef BOOL (WINAPI *PointerTo_CryptRetrieveTimeStamp)(LPCWSTR wszUrl,
                                                         PCCERT_CONTEXT *ppTsSigner,
                                                         HCERTSTORE phStore);
 
+namespace
+{
+/// Create payload for the 'signing-certificate' signed attribute.
+bool CreateSigningCertificateAttribute(vcl::PDFWriter::PDFSignContext& rContext, SvStream& rEncodedCertificate)
+{
+    // CryptEncodeObjectEx() does not support encoding arbitrary ASN.1
+    // structures, like SigningCertificateV2 from RFC 5035, so let's build it
+    // manually.
+
+    // Count the certificate hash and put it to aHash.
+    // 2.16.840.1.101.3.4.2.1, i.e. sha256.
+    std::vector<unsigned char> aSHA256{0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01};
+
+    HCRYPTPROV hProv = 0;
+    if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
+    {
+        SAL_WARN("vcl.pdfwriter", "CryptAcquireContext() failed");
+        return false;
+    }
+
+    HCRYPTHASH hHash = 0;
+    if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
+    {
+        SAL_WARN("vcl.pdfwriter", "CryptCreateHash() failed");
+        return false;
+    }
+
+    if (!CryptHashData(hHash, reinterpret_cast<const BYTE*>(rContext.m_pDerEncoded), rContext.m_nDerEncoded, 0))
+    {
+        SAL_WARN("vcl.pdfwriter", "CryptHashData() failed");
+        return false;
+    }
+
+    DWORD nHash = 0;
+    if (!CryptGetHashParam(hHash, HP_HASHVAL, nullptr, &nHash, 0))
+    {
+        SAL_WARN("vcl.pdfwriter", "CryptGetHashParam() failed to provide the hash length");
+        return false;
+    }
+
+    std::vector<unsigned char> aHash(nHash);
+    if (!CryptGetHashParam(hHash, HP_HASHVAL, aHash.data(), &nHash, 0))
+    {
+        SAL_WARN("vcl.pdfwriter", "CryptGetHashParam() failed to provide the hash");
+        return false;
+    }
+
+    CryptDestroyHash(hHash);
+    CryptReleaseContext(hProv, 0);
+
+    // We now have all the info to count the lengths.
+    // The layout of the payload is:
+    // SEQUENCE: SigningCertificateV2
+    //     SEQUENCE: SEQUENCE OF ESSCertIDv2
+    //      SEQUENCE: ESSCertIDv2
+    //          SEQUENCE: AlgorithmIdentifier
+    //              OBJECT: algorithm
+    //              NULL: parameters
+    //       OCTET STRING: certHash
+
+    size_t nAlgorithm = aSHA256.size() + 2;
+    size_t nParameters = 2;
+    size_t nAlgorithmIdentifier = nAlgorithm + nParameters + 2;
+    size_t nCertHash = aHash.size() + 2;
+    size_t nESSCertIDv2 = nAlgorithmIdentifier + nCertHash + 2;
+    size_t nESSCertIDv2s = nESSCertIDv2 + 2;
+
+    // Write SigningCertificateV2.
+    rEncodedCertificate.WriteUInt8(0x30);
+    rEncodedCertificate.WriteUInt8(nESSCertIDv2s);
+    // Write SEQUENCE OF ESSCertIDv2.
+    rEncodedCertificate.WriteUInt8(0x30);
+    rEncodedCertificate.WriteUInt8(nESSCertIDv2);
+    // Write ESSCertIDv2.
+    rEncodedCertificate.WriteUInt8(0x30);
+    rEncodedCertificate.WriteUInt8(nAlgorithmIdentifier + nCertHash);
+    // Write AlgorithmIdentifier.
+    rEncodedCertificate.WriteUInt8(0x30);
+    rEncodedCertificate.WriteUInt8(nAlgorithm + nParameters);
+    // Write algorithm.
+    rEncodedCertificate.WriteUInt8(0x06);
+    rEncodedCertificate.WriteUInt8(aSHA256.size());
+    rEncodedCertificate.WriteBytes(aSHA256.data(), aSHA256.size());
+    // Write parameters.
+    rEncodedCertificate.WriteUInt8(0x05);
+    rEncodedCertificate.WriteUInt8(0x00);
+    // Write certHash.
+    rEncodedCertificate.WriteUInt8(0x04);
+    rEncodedCertificate.WriteUInt8(aHash.size());
+    rEncodedCertificate.WriteBytes(aHash.data(), aHash.size());
+
+    return true;
+}
+} // anonymous namespace
+
 #endif
 
 bool PDFWriter::Sign(PDFSignContext& rContext)
@@ -7338,6 +7433,28 @@ bool PDFWriter::Sign(PDFSignContext& rContext)
     aSignerInfo.HashAlgorithm.pszObjId = const_cast<LPSTR>(szOID_NIST_sha256);
     aSignerInfo.HashAlgorithm.Parameters.cbData = 0;
 
+    // Add the signing certificate as a signed attribute.
+    CRYPT_INTEGER_BLOB aCertificateBlob;
+    SvMemoryStream aEncodedCertificate;
+    if (!CreateSigningCertificateAttribute(rContext, aEncodedCertificate))
+    {
+        SAL_WARN("vcl.pdfwriter", "CreateSigningCertificateAttribute() failed");
+        return false;
+    }
+    aCertificateBlob.pbData = const_cast<BYTE*>(static_cast<const BYTE*>(aEncodedCertificate.GetData()));
+    aCertificateBlob.cbData = aEncodedCertificate.GetSize();
+    CRYPT_ATTRIBUTE aCertificateAttribute;
+    /*
+     * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
+     * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
+     *   smime(16) id-aa(2) 47 }
+     */
+    aCertificateAttribute.pszObjId = const_cast<LPSTR>("1.2.840.113549.1.9.16.2.47");
+    aCertificateAttribute.cValue = 1;
+    aCertificateAttribute.rgValue = &aCertificateBlob;
+    aSignerInfo.cAuthAttr = 1;
+    aSignerInfo.rgAuthAttr = &aCertificateAttribute;
+
     CMSG_SIGNED_ENCODE_INFO aSignedInfo;
     memset(&aSignedInfo, 0, sizeof(aSignedInfo));
     aSignedInfo.cbSize = sizeof(aSignedInfo);
diff --git a/xmlsecurity/CppunitTest_xmlsecurity_signing.mk b/xmlsecurity/CppunitTest_xmlsecurity_signing.mk
index 24713cb..f7bbb0e 100644
--- a/xmlsecurity/CppunitTest_xmlsecurity_signing.mk
+++ b/xmlsecurity/CppunitTest_xmlsecurity_signing.mk
@@ -32,6 +32,7 @@ $(eval $(call gb_CppunitTest_use_libraries,xmlsecurity_signing, \
 
 $(eval $(call gb_CppunitTest_use_externals,xmlsecurity_signing,\
     boost_headers \
+    libxml2 \
 ))
 
 $(eval $(call gb_CppunitTest_set_include,xmlsecurity_signing,\
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/qa/unit/signing/data/certificate.crt b/xmlsecurity/qa/unit/signing/data/certificate.crt
deleted file mode 100644
index f3f34b7..0000000
--- a/xmlsecurity/qa/unit/signing/data/certificate.crt
+++ /dev/null
@@ -1,27 +0,0 @@
-MIIE7jCCAtagAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwVzELMAkGA1UEBhMCVUsx
-EDAOBgNVBAgMB0VuZ2xhbmQxEjAQBgNVBAoMCVRTQ1AgVGVzdDEiMCAGA1UEAwwZ
-VFNDUCBJbnRlcm1lZGlhdGUgUm9vdCBDQTAeFw0xNTEyMTgwNzU4MTlaFw0xNjEy
-MjcwNzU4MTlaMFUxCzAJBgNVBAYTAlVLMRAwDgYDVQQIDAdFbmdsYW5kMRIwEAYD
-VQQKDAlUU0NQIFRlc3QxIDAeBgNVBAMMF1RTQ1AgVGVzdCBleGFtcGxlIEFsaWNl
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3m2YNdX+nc1LkhlrNrcI
-PI3yCWnv0/0k9zDKpKiwjMH4vjWM46M6ptAiupxVpAMW5ojnhEyxaNHvZNsCwddY
-E6778hut2SJvz0szSBuHUuedcALI2EhVwdM0yLqfGo6WGeOIBDId49TemdNCMhk2
-zOpb1BqYhKls0LfdbxT/an3JaDmmLhPjvgYMJNYVX86L199OQFLJ1zLqQ0YirkKq
-XL9cSPmyYBKjgnqQ4Z5YfPL63EP0TsEfa5oQmy/0gS5FB2Wz9CqIptB130v0GR4X
-ObTpOkhPFfC5RDBFTMZoi4NCK10wn2NCbr7qZ3aMrOlfeKbsNIifwu0KYFHXyxL5
-AwIDAQABo4HFMIHCMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMDMGCWCG
-SAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBDbGllbnQgQ2VydGlmaWNhdGUw
-HQYDVR0OBBYEFCL6DzsuAbni8475Z+HkX5tv8iiWMB8GA1UdIwQYMBaAFMuejS1r
-WjUf3x1+2QbPSVpuXFl+MA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUEFjAUBggrBgEF
-BQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQELBQADggIBAFs0DeCDjttHQ0UHsYcn
-hfBCWRdOFdIr3F/IEbN2BL/grScGXoXRaYMIQJv/s5dKgZIuH7xMCVKazoftPVqU
-4bOEduAv0IJ6hQF/wEMBueA0UjvQQVYZgsOALi7TD3gYpFqYcH2Wfx5/5Ln6dllL
-8UsHoP+6gSLaYwjJd7FQ+IlNTzR65dRMLoJhoKqqyuM6cf/PM8sbK2NH2r8toypj
-fPixvD/w3wP7xn4oo/IGXcRK4DTHBF/rSMqeR6ePwXm5tVHrQBfnxN3dsGsXkQgq
-zBvvbPY0raraO4CPR7mZp4GVFHOsUNh5TI1SlfxWZ49HU3F5jWeiI9jPuw1RmuAy
-ZdFEt403Wi67v6revXe1By6UqIZjq3b2pJGBKZH+60P1cJScawzrN8pi1qQFV8Ji
-iJM6/MSciqplTT5F7SG0XZx1CjnBz5rMdYNhI9NNtF3oy9Xy9RvgYehFaC43ZlBB
-UMDmZFj5a78hOOkkq1UnrHUdeXyWhiEFzv5d8My2i0kWGq8r0HuC25BmOa17lHVx
-Q2o7Rdu9jDFP9oNizC7kQfA5QVRTfBFcWH7jml69RmVgfM+X+wdQgen9hJAILhBz
-mDfeteJ5ZEaoEYtw3isOGkpSyg7odjgYq7I+bOiN1toDg07vzfIkvF9KxlkDeRLX
-bmcFIvQsqFeF6cUwlZQYLOHA
diff --git a/xmlsecurity/qa/unit/signing/data/key3.db b/xmlsecurity/qa/unit/signing/data/key3.db
new file mode 100644
index 0000000..8ab32c2
Binary files /dev/null and b/xmlsecurity/qa/unit/signing/data/key3.db differ
diff --git a/xmlsecurity/qa/unit/signing/signing.cxx b/xmlsecurity/qa/unit/signing/signing.cxx
index 51e5366..5bcf0c2 100644
--- a/xmlsecurity/qa/unit/signing/signing.cxx
+++ b/xmlsecurity/qa/unit/signing/signing.cxx
@@ -15,6 +15,7 @@
 
 #include <test/bootstrapfixture.hxx>
 #include <unotest/macros_test.hxx>
+#include <test/xmltesttools.hxx>
 
 #include <com/sun/star/document/XStorageBasedDocument.hpp>
 #include <com/sun/star/embed/XStorage.hpp>
@@ -52,7 +53,7 @@ const char* DATA_DIRECTORY = "/xmlsecurity/qa/unit/signing/data/";
 }
 
 /// Testsuite for the document signing feature.
-class SigningTest : public test::BootstrapFixture, public unotest::MacrosTest
+class SigningTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
 {
     uno::Reference<uno::XComponentContext> mxComponentContext;
     uno::Reference<lang::XComponent> mxComponent;
@@ -61,6 +62,7 @@ public:
     SigningTest();
     virtual void setUp() override;
     virtual void tearDown() override;
+    void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override;
 
     void testDescription();
     /// Test a typical ODF where all streams are signed.
@@ -88,6 +90,7 @@ public:
 #endif
     void test96097Calc();
     void test96097Doc();
+    void testXAdES();
 
     CPPUNIT_TEST_SUITE(SigningTest);
     CPPUNIT_TEST(testDescription);
@@ -107,6 +110,7 @@ public:
 #endif
     CPPUNIT_TEST(test96097Calc);
     CPPUNIT_TEST(test96097Doc);
+    CPPUNIT_TEST(testXAdES);
     CPPUNIT_TEST_SUITE_END();
 
 private:
@@ -132,6 +136,7 @@ void SigningTest::setUp()
     OUString aTargetDir = m_directories.getURLFromWorkdir(
                               "/CppunitTest/xmlsecurity_signing.test.user/");
     osl::File::copy(aSourceDir + "cert8.db", aTargetDir + "cert8.db");
+    osl::File::copy(aSourceDir + "key3.db", aTargetDir + "key3.db");
     OUString aTargetPath;
     osl::FileBase::getSystemPathFromFileURL(aTargetDir, aTargetPath);
     setenv("MOZILLA_CERTIFICATE_FOLDER", aTargetPath.toUtf8().getStr(), 1);
@@ -168,20 +173,14 @@ void SigningTest::createCalc(const OUString& rURL)
 
 uno::Reference<security::XCertificate> SigningTest::getCertificate(DocumentSignatureManager& rSignatureManager)
 {
+    uno::Reference<security::XCertificate> xCertificate;
+
     uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = rSignatureManager.getSecurityEnvironment();
-    OUString aCertificate;
-    {
-        SvFileStream aStream(m_directories.getURLFromSrc(DATA_DIRECTORY) + "certificate.crt", StreamMode::READ);
-        OString aLine;
-        bool bMore = aStream.ReadLine(aLine);
-        while (bMore)
-        {
-            aCertificate += OUString::fromUtf8(aLine);
-            aCertificate += "\n";
-            bMore = aStream.ReadLine(aLine);
-        }
-    }
-    return xSecurityEnvironment->createCertificateFromAscii(aCertificate);
+    uno::Sequence<uno::Reference<security::XCertificate>> aCertificates = xSecurityEnvironment->getPersonalCertificates();
+    if (!aCertificates.hasElements())
+        return xCertificate;
+
+    return aCertificates[0];
 }
 
 void SigningTest::testDescription()
@@ -205,7 +204,8 @@ void SigningTest::testDescription()
 
     // Then add a signature document.
     uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager);
-    CPPUNIT_ASSERT(xCertificate.is());
+    if (!xCertificate.is())
+        return;
     OUString aDescription("SigningTest::testDescription");
     sal_Int32 nSecurityId;
     aManager.add(xCertificate, aDescription, nSecurityId, false);
@@ -238,7 +238,8 @@ void SigningTest::testOOXMLDescription()
 
     // Then add a document signature.
     uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager);
-    CPPUNIT_ASSERT(xCertificate.is());
+    if (!xCertificate.is())
+        return;
     OUString aDescription("SigningTest::testDescription");
     sal_Int32 nSecurityId;
     aManager.add(xCertificate, aDescription, nSecurityId, false);
@@ -271,7 +272,8 @@ void SigningTest::testOOXMLAppend()
 
     // Then add a second document signature.
     uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager);
-    CPPUNIT_ASSERT(xCertificate.is());
+    if (!xCertificate.is())
+        return;
     sal_Int32 nSecurityId;
     aManager.add(xCertificate, OUString(), nSecurityId, false);
 
@@ -297,7 +299,8 @@ void SigningTest::testOOXMLRemove()
 
     // Then remove the last added signature.
     uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager);
-    CPPUNIT_ASSERT(xCertificate.is());
+    if (!xCertificate.is())
+        return;
     aManager.remove(0);
 
     // Read back the signatures and make sure that only purpose1 is left.
@@ -327,7 +330,8 @@ void SigningTest::testOOXMLRemoveAll()
 
     // Then remove the only signature in the document.
     uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager);
-    CPPUNIT_ASSERT(xCertificate.is());
+    if (!xCertificate.is())
+        return;
     aManager.remove(0);
     aManager.read(/*bUseTempStream=*/true);
     aManager.write(/*bXAdESCompliantIfODF=*/false);
@@ -542,6 +546,58 @@ void SigningTest::test96097Doc()
     }
 }
 
+void SigningTest::testXAdES()
+{
+    // Create an empty document, store it to a tempfile and load it as a storage.
+    createDoc(OUString());
+
+    utl::TempFile aTempFile;
+    aTempFile.EnableKillingFile();
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    utl::MediaDescriptor aMediaDescriptor;
+    aMediaDescriptor["FilterName"] <<= OUString("writer8");
+    xStorable->storeAsURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+    DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
+    CPPUNIT_ASSERT(aManager.init());
+    uno::Reference <embed::XStorage> xStorage = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aTempFile.GetURL(), embed::ElementModes::READWRITE);
+    CPPUNIT_ASSERT(xStorage.is());
+    aManager.mxStore = xStorage;
+    aManager.maSignatureHelper.SetStorage(xStorage, "1.2");
+
+    // Create a signature.
+    uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager);
+    if (!xCertificate.is())
+        return;
+    sal_Int32 nSecurityId;
+    aManager.add(xCertificate, /*rDescription=*/OUString(), nSecurityId, /*bAdESCompliant=*/true);
+
+    // Write to storage.
+    aManager.read(/*bUseTempStream=*/true);
+    aManager.write(/*bXAdESCompliantIfODF=*/true);
+    uno::Reference<embed::XTransactedObject> xTransactedObject(xStorage, uno::UNO_QUERY);
+    xTransactedObject->commit();
+
+    // Parse the resulting XML.
+    uno::Reference<embed::XStorage> xMetaInf = xStorage->openStorageElement("META-INF", embed::ElementModes::READ);
+    uno::Reference<io::XInputStream> xInputStream(xMetaInf->openStreamElement("documentsignatures.xml", embed::ElementModes::READ), uno::UNO_QUERY);
+    std::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+    xmlDocPtr pXmlDoc = parseXmlStream(pStream.get());
+
+    // Assert that the digest algorithm is SHA-256 in the bAdESCompliant case, not SHA-1.
+    assertXPath(pXmlDoc, "/odfds:document-signatures/dsig:Signature/dsig:SignedInfo/dsig:Reference[@URI='content.xml']/dsig:DigestMethod", "Algorithm", ALGO_XMLDSIGSHA256);
+
+    // Assert that the digest of the signing certificate is included.
+    assertXPath(pXmlDoc, "//xd:CertDigest", 1);
+}
+
+void SigningTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx)
+{
+    xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("odfds"), BAD_CAST("urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0"));
+    xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("dsig"), BAD_CAST("http://www.w3.org/2000/09/xmldsig#"));
+    xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("xd"), BAD_CAST("http://uri.etsi.org/01903/v1.3.2#"));
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SigningTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index ef9900c..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
 {
@@ -375,13 +310,9 @@ sal_Int32 PDFDocument::WriteSignatureObject(const OUString& rDescription, bool b
     comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0');
     aSigBuffer.append(aContentFiller.makeStringAndClear());
     aSigBuffer.append(">\n/Type/Sig/SubFilter");
-#ifdef XMLSEC_CRYPTO_NSS
     if (bAdES)
         aSigBuffer.append("/ETSI.CAdES.detached");
     else
-#else
-        (void)bAdES;
-#endif
         aSigBuffer.append("/adbe.pkcs7.detached");
 
     // Time of signing.
@@ -2243,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");


More information about the Libreoffice-commits mailing list