include/sfx2/lokhelper.hxx        |    2 ++
 sfx2/qa/cppunit/view.cxx          |   32 ++++++++++++++++++++++++++++++++
 sfx2/source/view/lokhelper.cxx    |   36 ++++++++++++++++++++++++++++++++++++
 sw/source/uibase/uno/loktxdoc.cxx |   24 +++---------------------
 4 files changed, 73 insertions(+), 21 deletions(-)

New commits:
commit 39bf87f943cce9a0b5a784bc7426b5b98bbc6992
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu Nov 14 10:37:02 2024 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Thu Nov 14 17:28:21 2024 +0100

    cool#9992 lok doc sign, hash extract: add signatureTime parameter
    
    Execute getCommandValues('Signature') on the same document twice, you
    get different hashes, because the content includes a timestamp, which
    changes, so it's not possible to know if the hash is stable or not.
    
    Also, working with a provided timestamp will needed for
    
<https://docs.eideasy.com/electronic-signatures/api-flow-with-file-hashes-pdf.html#_4-add-the-signature-to-the-pdf-file>
    anyway.
    
    Fix the problem by adding a signatureTime parameter and this way we can
    have a test that makes sure we get the same hash if the time is
    provided.
    
    With this, the hash extraction part is meant to be ~complete.
    
    Change-Id: If5e1e5bcf84c3b777f26b2ded24dcca48ea9ad94
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176601
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx
index ca2212139cc8..7bbacdd127ba 100644
--- a/include/sfx2/lokhelper.hxx
+++ b/include/sfx2/lokhelper.hxx
@@ -253,6 +253,8 @@ public:
     static bool supportsCommand(std::u16string_view rCommand);
     /// Returns information about a given command in JSON format.
     static void getCommandValues(tools::JsonWriter& rJsonWriter, 
std::string_view rCommand);
+    /// Parses key-value pamaters of rCommand.
+    static std::map<OUString, OUString> 
parseCommandParameters(std::u16string_view rCommand);
 
 private:
     static int createView(SfxViewFrame& rViewFrame, ViewShellDocId docId);
diff --git a/sfx2/qa/cppunit/view.cxx b/sfx2/qa/cppunit/view.cxx
index e481d7186ce0..4068270350a6 100644
--- a/sfx2/qa/cppunit/view.cxx
+++ b/sfx2/qa/cppunit/view.cxx
@@ -143,6 +143,38 @@ CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, 
testLokHelperCommandValuesSignature)
     comphelper::Base64::decode(aBytes, aDigest);
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(32), aBytes.getLength());
 }
+
+namespace
+{
+OUString GetSignatureHash()
+{
+    tools::JsonWriter aWriter;
+    // Provide the current time, so the system timer is not contacted:
+    SfxLokHelper::getCommandValues(aWriter, 
".uno:Signature?signatureTime=1731329053152");
+    OString aJson = aWriter.finishAndGetAsOString();
+    std::stringstream aStream{ std::string(aJson) };
+    boost::property_tree::ptree aTree;
+    boost::property_tree::read_json(aStream, aTree);
+    auto it = aTree.find("digest");
+    CPPUNIT_ASSERT(it != aTree.not_found());
+    return OUString::fromUtf8(it->second.get_value<std::string>());
+}
+}
+
+CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, testLokHelperCommandValuesSignatureHash)
+{
+    // Given an unsigned PDF file:
+    loadFromFile(u"unsigned.pdf");
+
+    // When extracting hashes, two times:
+    OUString aHash1 = GetSignatureHash();
+    OUString aHash2 = GetSignatureHash();
+
+    // Then make sure that we get the same hash, since the same system time is 
provided:
+    // In case the test was slow enough that there was 1ms system time 
difference between the two
+    // calls, then this failed.
+    CPPUNIT_ASSERT_EQUAL(aHash1, aHash2);
+}
 #endif
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
index f8e819a2cb93..5bcb9ef22b98 100644
--- a/sfx2/source/view/lokhelper.cxx
+++ b/sfx2/source/view/lokhelper.cxx
@@ -43,6 +43,7 @@
 #include <comphelper/base64.hxx>
 #include <tools/json_writer.hxx>
 #include <svl/cryptosign.hxx>
+#include <tools/urlobj.hxx>
 
 #include <boost/property_tree/json_parser.hpp>
 
@@ -1000,6 +1001,33 @@ bool SfxLokHelper::supportsCommand(std::u16string_view 
rCommand)
     return std::find(vSupport.begin(), vSupport.end(), rCommand) != 
vSupport.end();
 }
 
+std::map<OUString, OUString> 
SfxLokHelper::parseCommandParameters(std::u16string_view rCommand)
+{
+    std::map<OUString, OUString> aMap;
+
+    INetURLObject aParser(rCommand);
+    OUString aArguments = aParser.GetParam();
+    sal_Int32 nParamIndex = 0;
+    do
+    {
+        std::u16string_view aParam = o3tl::getToken(aArguments, 0, '&', 
nParamIndex);
+        sal_Int32 nIndex = 0;
+        OUString aKey;
+        OUString aValue;
+        do
+        {
+            std::u16string_view aToken = o3tl::getToken(aParam, 0, '=', 
nIndex);
+            if (aKey.isEmpty())
+                aKey = aToken;
+            else
+                aValue = aToken;
+        } while (nIndex >= 0);
+        aMap[aKey] = INetURLObject::decode(aValue, 
INetURLObject::DecodeMechanism::WithCharset);
+    } while (nParamIndex >= 0);
+
+    return aMap;
+}
+
 void SfxLokHelper::getCommandValues(tools::JsonWriter& rJsonWriter, 
std::string_view rCommand)
 {
     static constexpr OStringLiteral aSignature(".uno:Signature");
@@ -1015,6 +1043,14 @@ void SfxLokHelper::getCommandValues(tools::JsonWriter& 
rJsonWriter, std::string_
     }
 
     svl::crypto::SigningContext aSigningContext;
+    std::map<OUString, OUString> aMap
+        = SfxLokHelper::parseCommandParameters(OUString::fromUtf8(rCommand));
+    auto it = aMap.find("signatureTime");
+    if (it != aMap.end())
+    {
+        // Signature time is provided: prefer it over the system time.
+        aSigningContext.m_nSignatureTime = it->second.toInt64();
+    }
     pObjectShell->SignDocumentContentUsingCertificate(aSigningContext);
     rJsonWriter.put("signatureTime", aSigningContext.m_nSignatureTime);
 
diff --git a/sw/source/uibase/uno/loktxdoc.cxx 
b/sw/source/uibase/uno/loktxdoc.cxx
index 7db06fe50a09..dc1c3ab74e57 100644
--- a/sw/source/uibase/uno/loktxdoc.cxx
+++ b/sw/source/uibase/uno/loktxdoc.cxx
@@ -30,6 +30,7 @@
 #include <tools/json_writer.hxx>
 #include <tools/urlobj.hxx>
 #include <xmloff/odffields.hxx>
+#include <sfx2/lokhelper.hxx>
 
 #include <IDocumentMarkAccess.hxx>
 #include <doc.hxx>
@@ -670,8 +671,6 @@ bool SwXTextDocument::supportsCommand(std::u16string_view 
rCommand)
 
 void SwXTextDocument::getCommandValues(tools::JsonWriter& rJsonWriter, 
std::string_view rCommand)
 {
-    std::map<OUString, OUString> aMap;
-
     static constexpr OStringLiteral aTextFormFields(".uno:TextFormFields");
     static constexpr OStringLiteral aTextFormField(".uno:TextFormField");
     static constexpr OStringLiteral 
aSetDocumentProperties(".uno:SetDocumentProperties");
@@ -682,25 +681,8 @@ void SwXTextDocument::getCommandValues(tools::JsonWriter& 
rJsonWriter, std::stri
     static constexpr OStringLiteral aField(".uno:Field");
     static constexpr OStringLiteral 
aExtractDocStructure(".uno:ExtractDocumentStructure");
 
-    INetURLObject aParser(OUString::fromUtf8(rCommand));
-    OUString aArguments = aParser.GetParam();
-    sal_Int32 nParamIndex = 0;
-    do
-    {
-        std::u16string_view aParam = o3tl::getToken(aArguments, 0, '&', 
nParamIndex);
-        sal_Int32 nIndex = 0;
-        OUString aKey;
-        OUString aValue;
-        do
-        {
-            std::u16string_view aToken = o3tl::getToken(aParam, 0, '=', 
nIndex);
-            if (aKey.isEmpty())
-                aKey = aToken;
-            else
-                aValue = aToken;
-        } while (nIndex >= 0);
-        aMap[aKey] = INetURLObject::decode(aValue, 
INetURLObject::DecodeMechanism::WithCharset);
-    } while (nParamIndex >= 0);
+    std::map<OUString, OUString> aMap
+        = SfxLokHelper::parseCommandParameters(OUString::fromUtf8(rCommand));
 
     if (o3tl::starts_with(rCommand, aTextFormFields))
     {

Reply via email to