[Libreoffice-commits] core.git: Branch 'libreoffice-6-0' - include/svl sc/CppunitTest_sc_subsequent_export_test.mk sc/inc sc/qa sc/source svl/source sw/CppunitTest_sw_odfexport.mk sw/qa xmloff/source

Michael Stahl mstahl at redhat.com
Thu Feb 15 10:18:10 UTC 2018


 include/svl/PasswordHelper.hxx                  |    7 +
 sc/CppunitTest_sc_subsequent_export_test.mk     |    2 
 sc/inc/tabprotection.hxx                        |    2 
 sc/qa/unit/data/fods/protection-key1.fods       |   20 ++++
 sc/qa/unit/data/fods/protection-key2.fods       |   20 ++++
 sc/qa/unit/data/fods/protection-key3.fods       |   20 ++++
 sc/qa/unit/data/fods/protection-key4.fods       |   20 ++++
 sc/qa/unit/data/fods/protection-key5.fods       |   20 ++++
 sc/qa/unit/subsequent_export-test.cxx           |  118 +++++++++++++++++++++++-
 sc/source/core/data/tabprotection.cxx           |   27 +++++
 sc/source/filter/xml/xmlexprt.cxx               |   25 +++++
 sc/source/ui/docshell/docsh.cxx                 |    4 
 svl/source/misc/PasswordHelper.cxx              |   52 +++++++++-
 sw/CppunitTest_sw_odfexport.mk                  |    1 
 sw/qa/extras/odfexport/data/protection-key.fodt |   48 +++++++++
 sw/qa/extras/odfexport/odfexport.cxx            |   32 ++++++
 xmloff/source/text/XMLSectionExport.cxx         |    8 +
 17 files changed, 416 insertions(+), 10 deletions(-)

New commits:
commit d28ceb7511a8df9345b1c9818baaad5532853a12
Author: Michael Stahl <mstahl at redhat.com>
Date:   Tue Feb 6 17:35:36 2018 +0100

    tdf#115483 svl xmloff sc sw: verify all ODF 1.2 protection-key hashes
    
    ODF 1.2 has added some mandatory requirements for protection-key hashes
    which did not exist in ODF 1.1.
    
    This affects sections and indexes in ODT documents, as well as
    spreadsheets and sheets in ODS documents.
    
    1. Accept the following hashed passwords:
        * UTF16 LE/BE encoded StarOffice-SHA1, OOo legacy and allowed by ODF 1.1
        * UTF8 encoded proper SHA1, as required by ODF 1.2
        * UTF8 encoded SHA256, as required by ODF 1.2
            - specified either with the wrong URL used in the ODF 1.2 spec
              or the correct URL from the W3C spec, see OFFICE-3702
        * Excel+SHA1 double-hash, only in Calc, see OFFICE-2112
    
    2. Round-trip any of the above as-is
        * for SHA256 only write the URL from the ODF 1.2 spec
    
    3. Generate only UTF16 LE encoded SHA1 for now, so that older LO
       releases can still verify the password
        * some time in the future, switch to generating some valid ODF 1.2 hash
    
    More changes are necessary in Calc, which can actually preserve
    different hashes for the same passwords in its runtime data model,
    whereas Writer just has a single buffer without even any metadata.
    
    For the Calc unit tests we need one document per hash because the
    protection-key attribute can be set on the entire spreadsheet, which
    is an unique element in the document.
    
    There are further uses of SvlPasswordHelper for change-tracking passwords,
    but apparently those are stored in settings.xml, so ODF has no
    requirements for them, so let's leave that as it is.
    
    Reviewed-on: https://gerrit.libreoffice.org/49352
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Michael Stahl <mstahl at redhat.com>
    (cherry picked from commit 398275ba9f4d65bebcc78864e70eee6212a84397)
    
    Change-Id: Icb720b14ae9c0d9c04d2e082769ae2b74e3af8aa
    Reviewed-on: https://gerrit.libreoffice.org/49390
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Eike Rathke <erack at redhat.com>

diff --git a/include/svl/PasswordHelper.hxx b/include/svl/PasswordHelper.hxx
index ca048ed85924..a71f2ca7c172 100644
--- a/include/svl/PasswordHelper.hxx
+++ b/include/svl/PasswordHelper.hxx
@@ -33,10 +33,15 @@ public:
     SVL_DLLPUBLIC static void     GetHashPassword(css::uno::Sequence <sal_Int8>& rPassHash, const sal_Char* pPass, sal_uInt32 nLen);
 
     SVL_DLLPUBLIC static void     GetHashPassword(css::uno::Sequence<sal_Int8>& rPassHash, const OUString& sPass);
+    SVL_DLLPUBLIC static void     GetHashPasswordSHA1UTF8(css::uno::Sequence<sal_Int8>& rPassHash, const OUString& sPass);
+    SVL_DLLPUBLIC static void     GetHashPasswordSHA256(css::uno::Sequence<sal_Int8>& rPassHash, const OUString& sPass);
     /**
     Use this method to compare a given string with another given Hash value.
     This is necessary, because in older versions exists different hashes of the same string. They were endian dependent.
-    We need this to handle old files. This method will compare against big and little endian. See #101326#
+    We need this to handle old files. This method will compare against big and
+    little endian UTF-16.
+    tdf#115483: also check 2 different new ways of hashing that were added in
+    ODF 1.2, requiring UTF-8 encoding.
     */
     SVL_DLLPUBLIC static bool     CompareHashPassword(const css::uno::Sequence<sal_Int8>& rOldPassHash, const OUString& sNewPass);
 };
diff --git a/sc/CppunitTest_sc_subsequent_export_test.mk b/sc/CppunitTest_sc_subsequent_export_test.mk
index b97423e27f89..cf6125f6ef04 100644
--- a/sc/CppunitTest_sc_subsequent_export_test.mk
+++ b/sc/CppunitTest_sc_subsequent_export_test.mk
@@ -72,6 +72,8 @@ $(eval $(call gb_CppunitTest_use_components,sc_subsequent_export_test,\
     embeddedobj/util/embobj \
     eventattacher/source/evtatt \
     filter/source/config/cache/filterconfig1 \
+	filter/source/odfflatxml/odfflatxml \
+	filter/source/xmlfilteradaptor/xmlfa \
     forms/util/frm \
     framework/util/fwk \
     i18npool/source/search/i18nsearch \
diff --git a/sc/inc/tabprotection.hxx b/sc/inc/tabprotection.hxx
index 61e0d605e6a8..3f2fc7b4e189 100644
--- a/sc/inc/tabprotection.hxx
+++ b/sc/inc/tabprotection.hxx
@@ -33,6 +33,8 @@ class ScTableProtectionImpl;
 enum ScPasswordHash
 {
     PASSHASH_SHA1 = 0,
+    PASSHASH_SHA1_UTF8, // tdf#115483 this is UTF8, previous one is wrong UTF16
+    PASSHASH_SHA256,
     PASSHASH_XL,
     PASSHASH_UNSPECIFIED
 };
diff --git a/sc/qa/unit/data/fods/protection-key1.fods b/sc/qa/unit/data/fods/protection-key1.fods
new file mode 100644
index 000000000000..e15f1e459919
--- /dev/null
+++ b/sc/qa/unit/data/fods/protection-key1.fods
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:scr
 ipt="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:form
 x="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">
+ <office:body>
+
+  <!-- UTF-16 LE, bad SHA1 -->
+  <office:spreadsheet table:structure-protected="true" table:protection-key="vbnhxyBKtPHCA1wB21zG1Oha8ZA=" table:protection-key-digest-algorithm="http://www.w3.org/2000/09/xmldsig#sha1">
+   <table:tracked-changes/>
+   <table:calculation-settings table:automatic-find-labels="false" table:use-regular-expressions="false" table:use-wildcards="true"/>
+
+   <table:table table:name="Sheet1" table:protected="true" table:protection-key="vbnhxyBKtPHCA1wB21zG1Oha8ZA=" table:protection-key-digest-algorithm="http://www.w3.org/2000/09/xmldsig#sha1">
+    <loext:table-protection loext:select-protected-cells="true" loext:select-unprotected-cells="true"/>
+    <table:table-row>
+     <table:table-cell/>
+    </table:table-row>
+   </table:table>
+   <table:named-expressions/>
+  </office:spreadsheet>
+ </office:body>
+</office:document>
diff --git a/sc/qa/unit/data/fods/protection-key2.fods b/sc/qa/unit/data/fods/protection-key2.fods
new file mode 100644
index 000000000000..b719a195b202
--- /dev/null
+++ b/sc/qa/unit/data/fods/protection-key2.fods
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:scr
 ipt="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:form
 x="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">
+ <office:body>
+
+  <!-- UTF-8, good SHA1 -->
+  <office:spreadsheet table:structure-protected="true" table:protection-key="nLHas0RIwepGDaH4c2hpyIUvIS8=" table:protection-key-digest-algorithm="http://www.w3.org/2000/09/xmldsig#sha1">
+   <table:tracked-changes/>
+   <table:calculation-settings table:automatic-find-labels="false" table:use-regular-expressions="false" table:use-wildcards="true"/>
+
+   <table:table table:name="Sheet1" table:protected="true" table:protection-key="nLHas0RIwepGDaH4c2hpyIUvIS8=" table:protection-key-digest-algorithm="http://www.w3.org/2000/09/xmldsig#sha1">
+    <loext:table-protection loext:select-protected-cells="true" loext:select-unprotected-cells="true"/>
+    <table:table-row>
+     <table:table-cell/>
+    </table:table-row>
+   </table:table>
+   <table:named-expressions/>
+  </office:spreadsheet>
+ </office:body>
+</office:document>
diff --git a/sc/qa/unit/data/fods/protection-key3.fods b/sc/qa/unit/data/fods/protection-key3.fods
new file mode 100644
index 000000000000..31c149fee0f6
--- /dev/null
+++ b/sc/qa/unit/data/fods/protection-key3.fods
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:scr
 ipt="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:form
 x="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">
+ <office:body>
+
+  <!-- UTF-8, SHA256, ODF 1.2 URI -->
+  <office:spreadsheet table:structure-protected="true" table:protection-key="1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=" table:protection-key-digest-algorithm="http://www.w3.org/2000/09/xmldsig#sha256">
+   <table:tracked-changes/>
+   <table:calculation-settings table:automatic-find-labels="false" table:use-regular-expressions="false" table:use-wildcards="true"/>
+
+   <table:table table:name="Sheet1" table:protected="true" table:protection-key="1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=" table:protection-key-digest-algorithm="http://www.w3.org/2000/09/xmldsig#sha256">
+    <loext:table-protection loext:select-protected-cells="true" loext:select-unprotected-cells="true"/>
+    <table:table-row>
+     <table:table-cell/>
+    </table:table-row>
+   </table:table>
+   <table:named-expressions/>
+  </office:spreadsheet>
+ </office:body>
+</office:document>
diff --git a/sc/qa/unit/data/fods/protection-key4.fods b/sc/qa/unit/data/fods/protection-key4.fods
new file mode 100644
index 000000000000..667a9e9b2525
--- /dev/null
+++ b/sc/qa/unit/data/fods/protection-key4.fods
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:scr
 ipt="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:form
 x="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">
+ <office:body>
+
+  <!-- UTF-8, SHA256, W3C URI -->
+  <office:spreadsheet table:structure-protected="true" table:protection-key="1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=" table:protection-key-digest-algorithm="http://www.w3.org/2001/04/xmlenc#sha256">
+   <table:tracked-changes/>
+   <table:calculation-settings table:automatic-find-labels="false" table:use-regular-expressions="false" table:use-wildcards="true"/>
+
+   <table:table table:name="Sheet1" table:protected="true" table:protection-key="1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=" table:protection-key-digest-algorithm="http://www.w3.org/2001/04/xmlenc#sha256">
+    <loext:table-protection loext:select-protected-cells="true" loext:select-unprotected-cells="true"/>
+    <table:table-row>
+     <table:table-cell/>
+    </table:table-row>
+   </table:table>
+   <table:named-expressions/>
+  </office:spreadsheet>
+ </office:body>
+</office:document>
diff --git a/sc/qa/unit/data/fods/protection-key5.fods b/sc/qa/unit/data/fods/protection-key5.fods
new file mode 100644
index 000000000000..b5dfcc83dd92
--- /dev/null
+++ b/sc/qa/unit/data/fods/protection-key5.fods
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:scr
 ipt="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:form
 x="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">
+ <office:body>
+
+  <!-- Excel hash + SHA1 -->
+  <office:spreadsheet table:structure-protected="true" table:protection-key="OX3WkEe79fv1PE+FUmfOLdwVoqI=" table:protection-key-digest-algorithm="http://docs.oasis-open.org/office/ns/table/legacy-hash-excel" loext:protection-key-digest-algorithm-2="http://www.w3.org/2000/09/xmldsig#sha1">
+   <table:tracked-changes/>
+   <table:calculation-settings table:automatic-find-labels="false" table:use-regular-expressions="false" table:use-wildcards="true"/>
+
+   <table:table table:name="Sheet1" table:style-name="ta1" table:protected="true" table:protection-key="OX3WkEe79fv1PE+FUmfOLdwVoqI=" table:protection-key-digest-algorithm="http://docs.oasis-open.org/office/ns/table/legacy-hash-excel" loext:protection-key-digest-algorithm-2="http://www.w3.org/2000/09/xmldsig#sha1">
+    <loext:table-protection loext:select-protected-cells="true" loext:select-unprotected-cells="true"/>
+    <table:table-row>
+     <table:table-cell/>
+    </table:table-row>
+   </table:table>
+   <table:named-expressions/>
+  </office:spreadsheet>
+ </office:body>
+</office:document>
diff --git a/sc/qa/unit/subsequent_export-test.cxx b/sc/qa/unit/subsequent_export-test.cxx
index 06ed9dcdf9ad..122fad288343 100644
--- a/sc/qa/unit/subsequent_export-test.cxx
+++ b/sc/qa/unit/subsequent_export-test.cxx
@@ -96,6 +96,11 @@ public:
     void testConditionalFormatExportODS();
     void testConditionalFormatExportXLSX();
     void testTdf99856_dataValidationTest();
+    void testProtectionKeyODS_UTF16LErtlSHA1();
+    void testProtectionKeyODS_UTF8SHA1();
+    void testProtectionKeyODS_UTF8SHA256ODF12();
+    void testProtectionKeyODS_UTF8SHA256W3C();
+    void testProtectionKeyODS_XL_SHA1();
     void testColorScaleExportODS();
     void testColorScaleExportXLSX();
     void testDataBarExportODS();
@@ -206,6 +211,11 @@ public:
     CPPUNIT_TEST(testConditionalFormatExportODS);
     CPPUNIT_TEST(testConditionalFormatExportXLSX);
     CPPUNIT_TEST(testTdf99856_dataValidationTest);
+    CPPUNIT_TEST(testProtectionKeyODS_UTF16LErtlSHA1);
+    CPPUNIT_TEST(testProtectionKeyODS_UTF8SHA1);
+    CPPUNIT_TEST(testProtectionKeyODS_UTF8SHA256ODF12);
+    CPPUNIT_TEST(testProtectionKeyODS_UTF8SHA256W3C);
+    CPPUNIT_TEST(testProtectionKeyODS_XL_SHA1);
     CPPUNIT_TEST(testColorScaleExportODS);
     CPPUNIT_TEST(testColorScaleExportXLSX);
     CPPUNIT_TEST(testDataBarExportODS);
@@ -328,7 +338,8 @@ void ScExportTest::registerNamespaces(xmlXPathContextPtr& pXmlXPathCtx)
         { BAD_CAST("xdr"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing") },
         { BAD_CAST("x"), BAD_CAST("http://schemas.openxmlformats.org/spreadsheetml/2006/main") },
         { BAD_CAST("r"), BAD_CAST("http://schemas.openxmlformats.org/package/2006/relationships") },
-        { BAD_CAST("number"), BAD_CAST("urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0") }
+        { BAD_CAST("number"), BAD_CAST("urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0") },
+        { BAD_CAST("loext"), BAD_CAST("urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0") },
     };
     for(size_t i = 0; i < SAL_N_ELEMENTS(aNamespaces); ++i)
     {
@@ -495,6 +506,111 @@ void ScExportTest::testTdf99856_dataValidationTest()
     xDocSh->DoClose();
 }
 
+void ScExportTest::testProtectionKeyODS_UTF16LErtlSHA1()
+{
+    OUString const password("1012345678901234567890123456789012345678901234567890");
+
+    ScDocShellRef xShell = loadDoc("protection-key1.", FORMAT_FODS);
+    CPPUNIT_ASSERT_MESSAGE("Failed to load doc", xShell.is());
+
+    ScDocument& rDoc = xShell->GetDocument();
+    ScDocProtection *const pDocProt(rDoc.GetDocProtection());
+    CPPUNIT_ASSERT(pDocProt->verifyPassword(password));
+    ScTableProtection *const pTabProt(rDoc.GetTabProtection(0));
+    CPPUNIT_ASSERT(pTabProt->verifyPassword(password));
+
+    // we can't assume that the user entered the password; check that we
+    // round-trip the password as-is
+    std::shared_ptr<utl::TempFile> pXPathFile = ScBootstrapFixture::exportTo(&(*xShell), FORMAT_ODS);
+    xmlDocPtr pXmlDoc = XPathHelper::parseExport(pXPathFile, m_xSFactory, "content.xml");
+    assertXPath(pXmlDoc, "//office:spreadsheet[@table:structure-protected='true' and @table:protection-key='vbnhxyBKtPHCA1wB21zG1Oha8ZA=' and @table:protection-key-digest-algorithm='http://www.w3.org/2000/09/xmldsig#sha1']");
+    assertXPath(pXmlDoc, "//table:table[@table:protected='true' and @table:protection-key='vbnhxyBKtPHCA1wB21zG1Oha8ZA=' and @table:protection-key-digest-algorithm='http://www.w3.org/2000/09/xmldsig#sha1']");
+}
+
+void ScExportTest::testProtectionKeyODS_UTF8SHA1()
+{
+    OUString const password("1012345678901234567890123456789012345678901234567890");
+
+    ScDocShellRef xShell = loadDoc("protection-key2.", FORMAT_FODS);
+    CPPUNIT_ASSERT_MESSAGE("Failed to load doc", xShell.is());
+
+    ScDocument& rDoc = xShell->GetDocument();
+    ScDocProtection *const pDocProt(rDoc.GetDocProtection());
+    CPPUNIT_ASSERT(pDocProt->verifyPassword(password));
+    ScTableProtection *const pTabProt(rDoc.GetTabProtection(0));
+    CPPUNIT_ASSERT(pTabProt->verifyPassword(password));
+
+    // we can't assume that the user entered the password; check that we
+    // round-trip the password as-is
+    std::shared_ptr<utl::TempFile> pXPathFile = ScBootstrapFixture::exportTo(&(*xShell), FORMAT_ODS);
+    xmlDocPtr pXmlDoc = XPathHelper::parseExport(pXPathFile, m_xSFactory, "content.xml");
+    assertXPath(pXmlDoc, "//office:spreadsheet[@table:structure-protected='true' and @table:protection-key='nLHas0RIwepGDaH4c2hpyIUvIS8=' and @table:protection-key-digest-algorithm='http://www.w3.org/2000/09/xmldsig#sha1']");
+    assertXPath(pXmlDoc, "//table:table[@table:protected='true' and @table:protection-key='nLHas0RIwepGDaH4c2hpyIUvIS8=' and @table:protection-key-digest-algorithm='http://www.w3.org/2000/09/xmldsig#sha1']");
+}
+
+void ScExportTest::testProtectionKeyODS_UTF8SHA256ODF12()
+{
+    OUString const password("1012345678901234567890123456789012345678901234567890");
+
+    ScDocShellRef xShell = loadDoc("protection-key3.", FORMAT_FODS);
+    CPPUNIT_ASSERT_MESSAGE("Failed to load doc", xShell.is());
+
+    ScDocument& rDoc = xShell->GetDocument();
+    ScDocProtection *const pDocProt(rDoc.GetDocProtection());
+    CPPUNIT_ASSERT(pDocProt->verifyPassword(password));
+    ScTableProtection *const pTabProt(rDoc.GetTabProtection(0));
+    CPPUNIT_ASSERT(pTabProt->verifyPassword(password));
+
+    // we can't assume that the user entered the password; check that we
+    // round-trip the password as-is
+    std::shared_ptr<utl::TempFile> pXPathFile = ScBootstrapFixture::exportTo(&(*xShell), FORMAT_ODS);
+    xmlDocPtr pXmlDoc = XPathHelper::parseExport(pXPathFile, m_xSFactory, "content.xml");
+    assertXPath(pXmlDoc, "//office:spreadsheet[@table:structure-protected='true' and @table:protection-key='1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=' and @table:protection-key-digest-algorithm='http://www.w3.org/2000/09/xmldsig#sha256']");
+    assertXPath(pXmlDoc, "//table:table[@table:protected='true' and @table:protection-key='1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=' and @table:protection-key-digest-algorithm='http://www.w3.org/2000/09/xmldsig#sha256']");
+}
+
+void ScExportTest::testProtectionKeyODS_UTF8SHA256W3C()
+{
+    OUString const password("1012345678901234567890123456789012345678901234567890");
+
+    ScDocShellRef xShell = loadDoc("protection-key4.", FORMAT_FODS);
+    CPPUNIT_ASSERT_MESSAGE("Failed to load doc", xShell.is());
+
+    ScDocument& rDoc = xShell->GetDocument();
+    ScDocProtection *const pDocProt(rDoc.GetDocProtection());
+    CPPUNIT_ASSERT(pDocProt->verifyPassword(password));
+    ScTableProtection *const pTabProt(rDoc.GetTabProtection(0));
+    CPPUNIT_ASSERT(pTabProt->verifyPassword(password));
+
+    // we can't assume that the user entered the password; check that we
+    // round-trip the password as-is
+    std::shared_ptr<utl::TempFile> pXPathFile = ScBootstrapFixture::exportTo(&(*xShell), FORMAT_ODS);
+    xmlDocPtr pXmlDoc = XPathHelper::parseExport(pXPathFile, m_xSFactory, "content.xml");
+    assertXPath(pXmlDoc, "//office:spreadsheet[@table:structure-protected='true' and @table:protection-key='1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=' and @table:protection-key-digest-algorithm='http://www.w3.org/2000/09/xmldsig#sha256']");
+    assertXPath(pXmlDoc, "//table:table[@table:protected='true' and @table:protection-key='1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=' and @table:protection-key-digest-algorithm='http://www.w3.org/2000/09/xmldsig#sha256']");
+}
+
+void ScExportTest::testProtectionKeyODS_XL_SHA1()
+{
+    OUString const password("1012345678901234567890123456789012345678901234567890");
+
+    ScDocShellRef xShell = loadDoc("protection-key5.", FORMAT_FODS);
+    CPPUNIT_ASSERT_MESSAGE("Failed to load doc", xShell.is());
+
+    ScDocument& rDoc = xShell->GetDocument();
+    ScDocProtection *const pDocProt(rDoc.GetDocProtection());
+    CPPUNIT_ASSERT(pDocProt->verifyPassword(password));
+    ScTableProtection *const pTabProt(rDoc.GetTabProtection(0));
+    CPPUNIT_ASSERT(pTabProt->verifyPassword(password));
+
+    // we can't assume that the user entered the password; check that we
+    // round-trip the password as-is
+    std::shared_ptr<utl::TempFile> pXPathFile = ScBootstrapFixture::exportTo(&(*xShell), FORMAT_ODS);
+    xmlDocPtr pXmlDoc = XPathHelper::parseExport(pXPathFile, m_xSFactory, "content.xml");
+    assertXPath(pXmlDoc, "//office:spreadsheet[@table:structure-protected='true' and @table:protection-key='OX3WkEe79fv1PE+FUmfOLdwVoqI=' and @table:protection-key-digest-algorithm='http://docs.oasis-open.org/office/ns/table/legacy-hash-excel' and @loext:protection-key-digest-algorithm-2='http://www.w3.org/2000/09/xmldsig#sha1']");
+    assertXPath(pXmlDoc, "//table:table[@table:protected='true' and @table:protection-key='OX3WkEe79fv1PE+FUmfOLdwVoqI=' and @table:protection-key-digest-algorithm='http://docs.oasis-open.org/office/ns/table/legacy-hash-excel' and @loext:protection-key-digest-algorithm-2='http://www.w3.org/2000/09/xmldsig#sha1']");
+}
+
 void ScExportTest::testColorScaleExportODS()
 {
     ScDocShellRef xShell = loadDoc("colorscale.", FORMAT_ODS);
diff --git a/sc/source/core/data/tabprotection.cxx b/sc/source/core/data/tabprotection.cxx
index 21a3f686a44f..9a4a8bdde26d 100644
--- a/sc/source/core/data/tabprotection.cxx
+++ b/sc/source/core/data/tabprotection.cxx
@@ -28,6 +28,8 @@
 #define DEBUG_TAB_PROTECTION 0
 
 #define URI_SHA1 "http://www.w3.org/2000/09/xmldsig#sha1"
+#define URI_SHA256_ODF12 "http://www.w3.org/2000/09/xmldsig#sha256"
+#define URI_SHA256_W3C "http://www.w3.org/2001/04/xmlenc#sha256"
 #define URI_XLS_LEGACY "http://docs.oasis-open.org/office/ns/table/legacy-hash-excel"
 
 using namespace ::com::sun::star;
@@ -62,6 +64,8 @@ OUString ScPassHashHelper::getHashURI(ScPasswordHash eHash)
 {
     switch (eHash)
     {
+        case PASSHASH_SHA256:
+            return OUString(URI_SHA256_ODF12);
         case PASSHASH_SHA1:
             return OUString(URI_SHA1);
         case PASSHASH_XL:
@@ -75,6 +79,8 @@ OUString ScPassHashHelper::getHashURI(ScPasswordHash eHash)
 
 ScPasswordHash ScPassHashHelper::getHashTypeFromURI(const OUString& rURI)
 {
+    if (rURI == URI_SHA256_ODF12 || rURI == URI_SHA256_W3C)
+        return PASSHASH_SHA256;
     if ( rURI == URI_SHA1 )
         return PASSHASH_SHA1;
     else if ( rURI == URI_XLS_LEGACY )
@@ -140,6 +146,12 @@ Sequence<sal_Int8> ScTableProtectionImpl::hashPassword(const OUString& aPassText
         case PASSHASH_SHA1:
             SvPasswordHelper::GetHashPassword(aHash, aPassText);
         break;
+        case PASSHASH_SHA1_UTF8:
+            SvPasswordHelper::GetHashPasswordSHA1UTF8(aHash, aPassText);
+        break;
+        case PASSHASH_SHA256:
+            SvPasswordHelper::GetHashPasswordSHA256(aHash, aPassText);
+        break;
         default:
             ;
     }
@@ -320,7 +332,20 @@ bool ScTableProtectionImpl::verifyPassword(const OUString& aPassText) const
     printf("\n");
 #endif
 
-    return aHash == maPassHash;
+    if (aHash == maPassHash)
+    {
+        return true;
+    }
+
+    // tdf#115483 compat hack for ODF 1.2; for now UTF8-SHA1 passwords are only
+    // verified, not generated
+    if (meHash1 == PASSHASH_SHA1 && meHash2 == PASSHASH_UNSPECIFIED)
+    {
+        Sequence<sal_Int8> const aHash2 = hashPassword(aPassText, PASSHASH_SHA1_UTF8);
+        return aHash2 == maPassHash;
+    }
+
+    return false;
 }
 
 bool ScTableProtectionImpl::isOptionEnabled(SCSIZE nOptId) const
diff --git a/sc/source/filter/xml/xmlexprt.cxx b/sc/source/filter/xml/xmlexprt.cxx
index 5db0920c020d..6026ec7e7483 100644
--- a/sc/source/filter/xml/xmlexprt.cxx
+++ b/sc/source/filter/xml/xmlexprt.cxx
@@ -1702,6 +1702,11 @@ void ScXMLExport::SetBodyAttributes()
                 aPassHash = p->getPasswordHash(PASSHASH_SHA1);
                 eHashUsed = PASSHASH_SHA1;
             }
+            else if (p->hasPasswordHash(PASSHASH_SHA256))
+            {
+                aPassHash = p->getPasswordHash(PASSHASH_SHA256);
+                eHashUsed = PASSHASH_SHA256;
+            }
             else if (p->hasPasswordHash(PASSHASH_XL, PASSHASH_SHA1))
             {
                 aPassHash = p->getPasswordHash(PASSHASH_XL, PASSHASH_SHA1);
@@ -1723,8 +1728,15 @@ void ScXMLExport::SetBodyAttributes()
                                 ScPassHashHelper::getHashURI(PASSHASH_SHA1));
                 }
                 else if (eHashUsed == PASSHASH_SHA1)
+                {
                     AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
                                  ScPassHashHelper::getHashURI(PASSHASH_SHA1));
+                }
+                else if (eHashUsed == PASSHASH_SHA256)
+                {
+                    AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+                                 ScPassHashHelper::getHashURI(PASSHASH_SHA256));
+                }
             }
         }
     }
@@ -2840,6 +2852,12 @@ void ScXMLExport::WriteTable(sal_Int32 nTable, const uno::Reference<sheet::XSpre
                         pProtect->getPasswordHash(PASSHASH_SHA1));
                     eHashUsed = PASSHASH_SHA1;
                 }
+                else if (pProtect->hasPasswordHash(PASSHASH_SHA256))
+                {
+                    ::sax::Converter::encodeBase64(aBuffer,
+                        pProtect->getPasswordHash(PASSHASH_SHA256));
+                    eHashUsed = PASSHASH_SHA256;
+                }
                 else if (pProtect->hasPasswordHash(PASSHASH_XL, PASSHASH_SHA1))
                 {
                     // Double-hash this by SHA1 on top of the legacy xls hash.
@@ -2861,8 +2879,15 @@ void ScXMLExport::WriteTable(sal_Int32 nTable, const uno::Reference<sheet::XSpre
                                         ScPassHashHelper::getHashURI(PASSHASH_SHA1));
                         }
                         else if (eHashUsed == PASSHASH_SHA1)
+                        {
                             AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
                                          ScPassHashHelper::getHashURI(PASSHASH_SHA1));
+                        }
+                        else if (eHashUsed == PASSHASH_SHA256)
+                        {
+                            AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+                                         ScPassHashHelper::getHashURI(PASSHASH_SHA256));
+                        }
                     }
                 }
             }
diff --git a/sc/source/ui/docshell/docsh.cxx b/sc/source/ui/docshell/docsh.cxx
index a5cea0179018..865e7c4ad2b1 100644
--- a/sc/source/ui/docshell/docsh.cxx
+++ b/sc/source/ui/docshell/docsh.cxx
@@ -1705,6 +1705,10 @@ bool ScDocShell::SaveAs( SfxMedium& rMedium )
     if (bNeedsRehash)
         // legacy xls hash double-hashed by SHA1 is also supported.
         bNeedsRehash = ScPassHashHelper::needsPassHashRegen(aDocument, PASSHASH_XL, PASSHASH_SHA1);
+    if (bNeedsRehash)
+    {   // SHA256 explicitly supported in ODF 1.2, implicitly in ODF 1.1
+        bNeedsRehash = ScPassHashHelper::needsPassHashRegen(aDocument, PASSHASH_SHA256);
+    }
 
     if (pViewShell && bNeedsRehash)
     {
diff --git a/svl/source/misc/PasswordHelper.cxx b/svl/source/misc/PasswordHelper.cxx
index 00558f6a1630..48aa165507ee 100644
--- a/svl/source/misc/PasswordHelper.cxx
+++ b/svl/source/misc/PasswordHelper.cxx
@@ -19,11 +19,32 @@
 
 
 #include <svl/PasswordHelper.hxx>
+#include <comphelper/hash.hxx>
 #include <rtl/digest.h>
 #include <memory>
 
 using namespace com::sun::star;
 
+void SvPasswordHelper::GetHashPasswordSHA256(uno::Sequence<sal_Int8>& rPassHash, OUString const& rPassword)
+{
+    OString const tmp(OUStringToOString(rPassword, RTL_TEXTENCODING_UTF8));
+    ::std::vector<unsigned char> const hash(::comphelper::Hash::calculateHash(
+        reinterpret_cast<unsigned char const*>(tmp.getStr()), tmp.getLength(),
+        ::comphelper::HashType::SHA256));
+    rPassHash.realloc(hash.size());
+    ::std::copy(hash.begin(), hash.end(), rPassHash.begin());
+}
+
+void SvPasswordHelper::GetHashPasswordSHA1UTF8(uno::Sequence<sal_Int8>& rPassHash, OUString const& rPassword)
+{
+    OString const tmp(OUStringToOString(rPassword, RTL_TEXTENCODING_UTF8));
+    ::std::vector<unsigned char> const hash(::comphelper::Hash::calculateHash(
+        reinterpret_cast<unsigned char const*>(tmp.getStr()), tmp.getLength(),
+        ::comphelper::HashType::SHA1));
+    rPassHash.realloc(hash.size());
+    ::std::copy(hash.begin(), hash.end(), rPassHash.begin());
+}
+
 void SvPasswordHelper::GetHashPassword(uno::Sequence<sal_Int8>& rPassHash, const sal_Char* pPass, sal_uInt32 nLen)
 {
     rPassHash.realloc(RTL_DIGEST_LENGTH_SHA1);
@@ -74,14 +95,31 @@ bool SvPasswordHelper::CompareHashPassword(const uno::Sequence<sal_Int8>& rOldPa
 {
     bool bResult = false;
 
-    uno::Sequence<sal_Int8> aNewPass(RTL_DIGEST_LENGTH_SHA1);
-    GetHashPasswordLittleEndian(aNewPass, sNewPass);
-    if (aNewPass == rOldPassHash)
-        bResult = true;
-    else
+    if (rOldPassHash.getLength() == RTL_DIGEST_LENGTH_SHA1)
+    {
+        uno::Sequence<sal_Int8> aNewPass(RTL_DIGEST_LENGTH_SHA1);
+        GetHashPasswordSHA1UTF8(aNewPass, sNewPass);
+        if (aNewPass == rOldPassHash)
+        {
+            bResult = true;
+        }
+        else
+        {
+            GetHashPasswordLittleEndian(aNewPass, sNewPass);
+            if (aNewPass == rOldPassHash)
+                bResult = true;
+            else
+            {
+                GetHashPasswordBigEndian(aNewPass, sNewPass);
+                bResult = (aNewPass == rOldPassHash);
+            }
+        }
+    }
+    else if (rOldPassHash.getLength() == 32)
     {
-        GetHashPasswordBigEndian(aNewPass, sNewPass);
-        bResult = (aNewPass == rOldPassHash);
+        uno::Sequence<sal_Int8> aNewPass;
+        GetHashPasswordSHA256(aNewPass, sNewPass);
+        bResult = aNewPass == rOldPassHash;
     }
 
     return bResult;
diff --git a/sw/CppunitTest_sw_odfexport.mk b/sw/CppunitTest_sw_odfexport.mk
index e8c43ad931c9..343ab0a58066 100644
--- a/sw/CppunitTest_sw_odfexport.mk
+++ b/sw/CppunitTest_sw_odfexport.mk
@@ -21,6 +21,7 @@ $(eval $(call gb_CppunitTest_use_libraries,sw_odfexport, \
     cppuhelper \
     sal \
 	sfx \
+	svl \
 	sw \
     test \
     tl \
diff --git a/sw/qa/extras/odfexport/data/protection-key.fodt b/sw/qa/extras/odfexport/data/protection-key.fodt
new file mode 100644
index 000000000000..cd4cde9623a4
--- /dev/null
+++ b/sw/qa/extras/odfexport/data/protection-key.fodt
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oas
 is:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:
 experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+
+ <office:styles>
+
+  <style:style style:name="Standard" style:family="paragraph" style:class="text"/>
+
+ </office:styles>
+ <office:automatic-styles>
+
+  <style:style style:name="Sect" style:family="section">
+   <style:section-properties style:editable="false">
+    <style:columns fo:column-count="1" fo:column-gap="0cm"/>
+   </style:section-properties>
+  </style:style>
+
+ </office:automatic-styles>
+ <office:body>
+  <office:text>
+
+   <!-- UTF-16 LE, bad SHA1 -->
+   <text:section text:style-name="Sect" text:name="Section0" text:protected="true" text:protection-key="vbnhxyBKtPHCA1wB21zG1Oha8ZA=">
+    <text:p text:style-name="Standard"/>
+   </text:section>
+
+   <!-- echo -n $(echo -n 1012345678901234567890123456789012345678901234567890 | sha1sum | cut -f 1 -d ' ') | xxd -r -p | base64 -->
+   <!-- UTF-8, good SHA1 -->
+   <text:section text:style-name="Sect" text:name="Section1" text:protected="true" text:protection-key="nLHas0RIwepGDaH4c2hpyIUvIS8=">
+    <text:p text:style-name="Standard"/>
+   </text:section>
+
+   <!-- echo -n $(echo -n 1012345678901234567890123456789012345678901234567890 | sha256sum | cut -f 1 -d ' ') | xxd -r -p | base64 -->
+   <!-- UTF-8, SHA256, ODF 1.2 URI -->
+   <text:section text:style-name="Sect" text:name="Section2" text:protected="true" text:protection-key="1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=" text:protection-key-digest-algorithm="http://www.w3.org/2000/09/xmldsig#sha256">
+    <text:p text:style-name="Standard"/>
+   </text:section>
+
+   <!-- UTF-8, SHA256, W3C URI -->
+   <text:section text:style-name="Sect" text:name="Section3" text:protected="true" text:protection-key="1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=" text:protection-key-digest-algorithm="http://www.w3.org/2001/04/xmlenc#sha256">
+    <text:p text:style-name="Standard"/>
+   </text:section>
+
+   <text:p text:style-name="Standard"/>
+  </office:text>
+ </office:body>
+</office:document>
+
diff --git a/sw/qa/extras/odfexport/odfexport.cxx b/sw/qa/extras/odfexport/odfexport.cxx
index 1e7b1badbe06..27762d0bf60b 100644
--- a/sw/qa/extras/odfexport/odfexport.cxx
+++ b/sw/qa/extras/odfexport/odfexport.cxx
@@ -36,6 +36,7 @@
 #include <comphelper/fileformat.h>
 #include <comphelper/propertysequence.hxx>
 #include <unotools/streamwrap.hxx>
+#include <svl/PasswordHelper.hxx>
 
 class Test : public SwModelTestBase
 {
@@ -748,6 +749,37 @@ DECLARE_ODFEXPORT_TEST(testCharacterBorder, "charborder.odt")
     }
 }
 
+DECLARE_ODFEXPORT_TEST(testProtectionKey, "protection-key.fodt")
+{
+    OUString const password("1012345678901234567890123456789012345678901234567890");
+
+    // check 1 invalid OOo legacy password and 3 valid ODF 1.2 passwords
+    uno::Reference<text::XTextSectionsSupplier> xTextSectionsSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> xSections(xTextSectionsSupplier->getTextSections(), uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> xSect0(xSections->getByIndex(0), uno::UNO_QUERY);
+    uno::Sequence<sal_Int8> const key0(getProperty<uno::Sequence<sal_Int8>>(xSect0, "ProtectionKey"));
+    CPPUNIT_ASSERT(SvPasswordHelper::CompareHashPassword(key0, password));
+    uno::Reference<beans::XPropertySet> xSect1(xSections->getByIndex(1), uno::UNO_QUERY);
+    uno::Sequence<sal_Int8> const key1(getProperty<uno::Sequence<sal_Int8>>(xSect1, "ProtectionKey"));
+    CPPUNIT_ASSERT(SvPasswordHelper::CompareHashPassword(key1, password));
+    uno::Reference<beans::XPropertySet> xSect2(xSections->getByIndex(2), uno::UNO_QUERY);
+    uno::Sequence<sal_Int8> const key2(getProperty<uno::Sequence<sal_Int8>>(xSect1, "ProtectionKey"));
+    CPPUNIT_ASSERT(SvPasswordHelper::CompareHashPassword(key2, password));
+    uno::Reference<beans::XPropertySet> xSect3(xSections->getByIndex(3), uno::UNO_QUERY);
+    uno::Sequence<sal_Int8> const key3(getProperty<uno::Sequence<sal_Int8>>(xSect1, "ProtectionKey"));
+    CPPUNIT_ASSERT(SvPasswordHelper::CompareHashPassword(key3, password));
+
+    // we can't assume that the user entered the password; check that we
+    // round-trip the password as-is
+    if (xmlDocPtr pXmlDoc = parseExport("content.xml"))
+    {
+        assertXPath(pXmlDoc, "//text:section[@text:name='Section0' and @text:protected='true' and @text:protection-key='vbnhxyBKtPHCA1wB21zG1Oha8ZA=']");
+        assertXPath(pXmlDoc, "//text:section[@text:name='Section1' and @text:protected='true' and @text:protection-key='nLHas0RIwepGDaH4c2hpyIUvIS8=']");
+        assertXPath(pXmlDoc, "//text:section[@text:name='Section2' and @text:protected='true' and @text:protection-key-digest-algorithm='http://www.w3.org/2000/09/xmldsig#sha256' and @text:protection-key='1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=']");
+        assertXPath(pXmlDoc, "//text:section[@text:name='Section3' and @text:protected='true' and @text:protection-key-digest-algorithm='http://www.w3.org/2000/09/xmldsig#sha256' and @text:protection-key='1tnJohagR2T0yF/v69hLPuumSTsj32CumW97nkKGuSQ=']");
+    }
+}
+
 DECLARE_ODFEXPORT_TEST(testFdo43807, "fdo43807.odt")
 {
     uno::Reference<beans::XPropertySet> xSet(getParagraph(1), uno::UNO_QUERY);
diff --git a/xmloff/source/text/XMLSectionExport.cxx b/xmloff/source/text/XMLSectionExport.cxx
index 899cb51bc068..1413d2d791a5 100644
--- a/xmloff/source/text/XMLSectionExport.cxx
+++ b/xmloff/source/text/XMLSectionExport.cxx
@@ -398,8 +398,16 @@ void XMLSectionExport::ExportRegularSectionStart(
     {
         OUStringBuffer aBuffer;
         ::sax::Converter::encodeBase64(aBuffer, aPassword);
+        // in ODF 1.0/1.1 the algorithm was left unspecified so we can write anything
         GetExport().AddAttribute(XML_NAMESPACE_TEXT, XML_PROTECTION_KEY,
                                  aBuffer.makeStringAndClear());
+        if (aPassword.getLength() == 32 && GetExport().getDefaultVersion() >= SvtSaveOptions::ODFVER_012)
+        {
+            // attribute exists in ODF 1.2 or later; default is SHA1 so no need to write that
+            GetExport().AddAttribute(XML_NAMESPACE_TEXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+                    // write the URL from ODF 1.2, not the W3C one
+                    "http://www.w3.org/2000/09/xmldsig#sha256");
+        }
     }
 
     // export element


More information about the Libreoffice-commits mailing list