include/svl/cryptosign.hxx             |    2 ++
 include/vcl/filter/pdfdocument.hxx     |    3 ++-
 include/vcl/pdfwriter.hxx              |    6 +++++-
 sfx2/qa/cppunit/view.cxx               |   11 ++++++++++-
 sfx2/source/view/lokhelper.cxx         |    6 ++++++
 svl/source/crypto/cryptosign.cxx       |   13 +++++++++++--
 vcl/source/filter/ipdf/pdfdocument.cxx |   10 ++++++----
 vcl/source/gdi/pdfwriter_impl.cxx      |   16 +++++++++++++++-
 8 files changed, 57 insertions(+), 10 deletions(-)

New commits:
commit f5f672ba45abd146d9aefe1a5a8d8c87ccfa4cfa
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Tue Nov 12 08:19:50 2024 +0100
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Tue Nov 12 10:10:20 2024 +0100

    cool#9992 lok doc sign, hash extract: digest for 
getCommandValues('Signature')
    
    To be able to sign externally, we need a way to know what is the
    document hash that would be passed to
    NSS_CMSSignedData_SetDigestValue(), without actually performing the
    signing.
    
    Note that svl::crypto::SigningContext already gives us a way to expose
    the time that would be used for signing.
    
    Expose the hash in a similar way: the format is a SHA-256 hash in base64
    form.
    
    This adapts both places dealing with time: vcl::PDFWriter::GetDateTime()
    and svl::crypto::Signing::Sign, to make sure they use the same time,
    otherwise the hash would potentially depend on two times, which would be
    hard to reproduce later when we serialize the signature we get.
    
    Change-Id: Ib039db4cdd043c8117215c31cb5bc83397693820
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176459
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>

diff --git a/include/svl/cryptosign.hxx b/include/svl/cryptosign.hxx
index a547036813c1..dce75dad8fcf 100644
--- a/include/svl/cryptosign.hxx
+++ b/include/svl/cryptosign.hxx
@@ -103,6 +103,8 @@ public:
     /// If m_xCertificate is not set, the time that would be used, in 
milliseconds since the epoch
     /// (1970-01-01 UTC).
     sal_Int64 m_nSignatureTime = 0;
+    /// SHA-256 digest.
+    std::vector<unsigned char> m_aDigest;
 };
 
 }
diff --git a/include/vcl/filter/pdfdocument.hxx 
b/include/vcl/filter/pdfdocument.hxx
index c91b2d6cb608..3081100999b0 100644
--- a/include/vcl/filter/pdfdocument.hxx
+++ b/include/vcl/filter/pdfdocument.hxx
@@ -531,7 +531,8 @@ class VCL_DLLPUBLIC PDFDocument final : public 
PDFObjectContainer
     /// Suggest a minimal, yet free signature ID to use for the next signature.
     sal_uInt32 GetNextSignature();
     /// Write the signature object as part of signing.
-    sal_Int32 WriteSignatureObject(const OUString& rDescription, bool bAdES,
+    sal_Int32 WriteSignatureObject(svl::crypto::SigningContext& 
rSigningContext,
+                                   const OUString& rDescription, bool bAdES,
                                    sal_uInt64& rLastByteRangeOffset, 
sal_Int64& rContentOffset);
     /// Write the appearance object as part of signing.
     sal_Int32 WriteAppearanceObject(tools::Rectangle& rSignatureRectangle);
diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx
index 7f041fda0716..57457f1a95bb 100644
--- a/include/vcl/pdfwriter.hxx
+++ b/include/vcl/pdfwriter.hxx
@@ -49,6 +49,10 @@ namespace tools {
     class Polygon;
     class PolyPolygon;
 }
+namespace svl::crypto
+{
+class SigningContext;
+}
 class Bitmap;
 class BitmapEx;
 class Gradient;
@@ -1259,7 +1263,7 @@ The following structure describes the permissions used in 
PDF security
     static void AppendUnicodeTextString(const OUString& rString, 
OStringBuffer& rBuffer);
 
     /// Get current date/time in PDF D:YYYYMMDDHHMMSS form.
-    static OString GetDateTime();
+    static OString GetDateTime(svl::crypto::SigningContext* pSigningContext = 
nullptr);
 };
 
 }
diff --git a/sfx2/qa/cppunit/view.cxx b/sfx2/qa/cppunit/view.cxx
index bd8100702be0..4f049b22ae88 100644
--- a/sfx2/qa/cppunit/view.cxx
+++ b/sfx2/qa/cppunit/view.cxx
@@ -24,6 +24,8 @@
 #include <sfx2/lokhelper.hxx>
 #include <sfx2/sfxbasemodel.hxx>
 #include <tools/json_writer.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/base64.hxx>
 
 using namespace com::sun::star;
 
@@ -121,7 +123,7 @@ CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, 
testLokHelperCommandValuesSignature)
     SfxLokHelper::getCommandValues(aWriter, ".uno:Signature");
     OString aJson = aWriter.finishAndGetAsOString();
 
-    // Then make sure that we get a signature time:
+    // Then make sure that we get a signature time and a hash:
     CPPUNIT_ASSERT(SfxLokHelper::supportsCommand(u"Signature"));
     std::stringstream aStream{ std::string(aJson) };
     boost::property_tree::ptree aTree;
@@ -131,6 +133,13 @@ CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, 
testLokHelperCommandValuesSignature)
     CPPUNIT_ASSERT(it != aTree.not_found());
     auto nSignatureTime = it->second.get_value<sal_Int64>();
     CPPUNIT_ASSERT(nSignatureTime != 0);
+    // Base64 encoded hash, that has the SHA-256 length:
+    it = aTree.find("digest");
+    CPPUNIT_ASSERT(it != aTree.not_found());
+    auto aDigest = OUString::fromUtf8(it->second.get_value<std::string>());
+    uno::Sequence<sal_Int8> aBytes;
+    comphelper::Base64::decode(aBytes, aDigest);
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(32), aBytes.getLength());
 }
 #endif
 
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
index 399a2e386b2b..d22f13f726b8 100644
--- a/sfx2/source/view/lokhelper.cxx
+++ b/sfx2/source/view/lokhelper.cxx
@@ -1015,6 +1015,12 @@ void SfxLokHelper::getCommandValues(tools::JsonWriter& 
rJsonWriter, std::string_
     svl::crypto::SigningContext aSigningContext;
     pObjectShell->SignDocumentContentUsingCertificate(aSigningContext);
     rJsonWriter.put("signatureTime", aSigningContext.m_nSignatureTime);
+
+    uno::Sequence<sal_Int8> 
aDigest(reinterpret_cast<sal_Int8*>(aSigningContext.m_aDigest.data()),
+                                    aSigningContext.m_aDigest.size());
+    OUStringBuffer aBuffer;
+    comphelper::Base64::encode(aBuffer, aDigest);
+    rJsonWriter.put("digest", aBuffer.makeStringAndClear());
 }
 
 void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType)
diff --git a/svl/source/crypto/cryptosign.cxx b/svl/source/crypto/cryptosign.cxx
index eb95a4a9e0bf..3054871baf45 100644
--- a/svl/source/crypto/cryptosign.cxx
+++ b/svl/source/crypto/cryptosign.cxx
@@ -977,10 +977,19 @@ bool Signing::Sign(OStringBuffer& rCMSHexBuffer)
 
     PRTime now = PR_Now();
 
-    if (!m_rSigningContext.m_xCertificate.is())
+    // The context unit is milliseconds, PR_Now() unit is microseconds.
+    if (m_rSigningContext.m_nSignatureTime)
+    {
+        now = m_rSigningContext.m_nSignatureTime * 1000;
+    }
+    else
     {
-        // The context unit is milliseconds, PR_Now() unit is microseconds.
         m_rSigningContext.m_nSignatureTime = now / 1000;
+    }
+
+    if (!m_rSigningContext.m_xCertificate.is())
+    {
+        m_rSigningContext.m_aDigest = aHashResult;
         // No certificate is provided: don't actually sign -- just update the 
context with the
         // parameters for the signing and return.
         return false;
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx 
b/vcl/source/filter/ipdf/pdfdocument.cxx
index 63c3424be3d1..3e4ce6e7c696 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -122,7 +122,8 @@ sal_uInt32 PDFDocument::GetNextSignature()
     return nRet + 1;
 }
 
-sal_Int32 PDFDocument::WriteSignatureObject(const OUString& rDescription, bool 
bAdES,
+sal_Int32 PDFDocument::WriteSignatureObject(svl::crypto::SigningContext& 
rSigningContext,
+                                            const OUString& rDescription, bool 
bAdES,
                                             sal_uInt64& rLastByteRangeOffset,
                                             sal_Int64& rContentOffset)
 {
@@ -146,7 +147,7 @@ sal_Int32 PDFDocument::WriteSignatureObject(const OUString& 
rDescription, bool b
         aSigBuffer.append("/adbe.pkcs7.detached");
 
     // Time of signing.
-    aSigBuffer.append(" /M (" + vcl::PDFWriter::GetDateTime()
+    aSigBuffer.append(" /M (" + vcl::PDFWriter::GetDateTime(&rSigningContext)
                       + ")"
 
                         // Byte range: we can write offset1-length1 and 
offset2 right now, will
@@ -863,8 +864,9 @@ bool PDFDocument::Sign(svl::crypto::SigningContext& 
rSigningContext, const OUStr
 
     sal_uInt64 nSignatureLastByteRangeOffset = 0;
     sal_Int64 nSignatureContentOffset = 0;
-    sal_Int32 nSignatureId = WriteSignatureObject(
-        rDescription, bAdES, nSignatureLastByteRangeOffset, 
nSignatureContentOffset);
+    sal_Int32 nSignatureId
+        = WriteSignatureObject(rSigningContext, rDescription, bAdES, 
nSignatureLastByteRangeOffset,
+                               nSignatureContentOffset);
 
     tools::Rectangle aSignatureRectangle;
     sal_Int32 nAppearanceId = WriteAppearanceObject(aSignatureRectangle);
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index 0b29ed97b75b..bdf546bb3bab 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -1475,13 +1475,27 @@ void PDFWriterImpl::setupDocInfo()
         m_aContext.Encryption.DocumentIdentifier = aId;
 }
 
-OString PDFWriter::GetDateTime()
+OString PDFWriter::GetDateTime(svl::crypto::SigningContext* pSigningContext)
 {
     OStringBuffer aRet;
 
     TimeValue aTVal, aGMT;
     oslDateTime aDT;
     osl_getSystemTime(&aGMT);
+
+    if (pSigningContext)
+    {
+        // The context unit is milliseconds, TimeValue is seconds + 
nanoseconds.
+        if (pSigningContext->m_nSignatureTime)
+        {
+            aGMT = 
std::chrono::milliseconds(pSigningContext->m_nSignatureTime);
+        }
+        else
+        {
+            pSigningContext->m_nSignatureTime = 
static_cast<sal_Int64>(aGMT.Seconds) * 1000 + aGMT.Nanosec / 1000000;
+        }
+    }
+
     osl_getLocalTimeFromSystemTime(&aGMT, &aTVal);
     osl_getDateTimeFromTimeValue(&aTVal, &aDT);
 

Reply via email to