include/sfx2/docfile.hxx                 |    6 -
 package/source/zippackage/ZipPackage.cxx |    8 +
 sfx2/source/appl/appopen.cxx             |   18 ++-
 sfx2/source/doc/docfile.cxx              |  172 ++++++++++++++++++++++++++--
 sfx2/source/doc/objmisc.cxx              |    2 
 sfx2/source/doc/objserv.cxx              |    9 +
 sfx2/source/doc/objstor.cxx              |  186 ++++++++++++++++++++++++-------
 7 files changed, 337 insertions(+), 64 deletions(-)

New commits:
commit 7101c620857cb885076b85cd1447e50d30cab528
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Wed Dec 6 20:19:15 2023 +0100
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Thu Dec 7 09:28:50 2023 +0100

    sfx2: simplify SetCommonStorageEncryptionData() calls
    
    Some of these look redundant, as SfxMedium::GetStorage() already does
    this earlier.
    
    Change-Id: Iba178196898adfd4ee260da908c7298f3f2c1734
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160408
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx
index c801fb52bc41..411c980b6e09 100644
--- a/sfx2/source/doc/docfile.cxx
+++ b/sfx2/source/doc/docfile.cxx
@@ -1869,6 +1869,11 @@ uno::Reference < embed::XStorage > 
SfxMedium::GetStorage( bool bCreateTempFile )
         }
     }
 
+    if (GetErrorCode()) // decryption failed?
+    {
+        pImpl->xStorage.clear();
+    }
+
     // TODO/LATER: Get versionlist on demand
     if ( pImpl->xStorage.is() )
     {
diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx
index 9ae2c8faad63..598b85aa07e8 100644
--- a/sfx2/source/doc/objstor.cxx
+++ b/sfx2/source/doc/objstor.cxx
@@ -1012,26 +1012,17 @@ bool SfxObjectShell::DoSave()
             }
         }
 
-        uno::Sequence< beans::NamedValue > aEncryptionData;
         if ( IsPackageStorageFormat_Impl( *GetMedium() ) )
         {
-            if ( GetEncryptionData_Impl( &GetMedium()->GetItemSet(), 
aEncryptionData ) )
+            GetMedium()->GetStorage(); // sets encryption properties if 
necessary
+            if (GetMedium()->GetErrorCode())
             {
-                try
-                {
-                    //TODO/MBA: GetOutputStorage?! Special mode, because it's 
"Save"?!
-                    
::comphelper::OStorageHelper::SetCommonStorageEncryptionData( 
GetMedium()->GetStorage(), aEncryptionData );
-                    bOk = true;
-                }
-                catch( uno::Exception& )
-                {
-                    SetError(ERRCODE_IO_GENERAL);
-                }
-
-                DBG_ASSERT( bOk, "The root storage must allow to set common 
password!\n" );
+                SetError(ERRCODE_IO_GENERAL);
             }
             else
+            {
                 bOk = true;
+            }
 #if HAVE_FEATURE_SCRIPTING
             if ( HasBasic() )
             {
@@ -1471,12 +1462,12 @@ bool SfxObjectShell::SaveTo_Impl
     bCopyTo =   GetCreateMode() == SfxObjectCreateMode::EMBEDDED ||
                 (pSaveToItem && pSaveToItem->GetValue());
 
-    bool bOk = false;
+    bool bOk = true;
     // TODO/LATER: get rid of bOk
     if (bOwnTarget && pFilter && !(pFilter->GetFilterFlags() & 
SfxFilterFlags::STARONEFILTER))
     {
         uno::Reference< embed::XStorage > xMedStorage = rMedium.GetStorage();
-        if ( !xMedStorage.is() )
+        if (!xMedStorage.is() || rMedium.GetErrorCode())
         {
             // no saving without storage
             pImpl->bForbidReload = bOldStat;
@@ -1484,31 +1475,7 @@ bool SfxObjectShell::SaveTo_Impl
         }
 
         // transfer password from the parameters to the storage
-        bool bPasswdProvided = false;
-        if (aEncryptionData.getLength() != 0)
-        {
-            bPasswdProvided = true;
-            if (xODFDecryptedInnerPackageStream.is())
-            {
-                bOk = true;
-            }
-            else
-            {
-                // TODO: GetStorage() already did that?
-                try {
-                    
::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xMedStorage, 
aEncryptionData );
-                    bOk = true;
-                }
-                catch( uno::Exception& )
-                {
-                    SAL_WARN( "sfx.doc", "Setting of common encryption key 
failed!" );
-                    SetError(ERRCODE_IO_GENERAL);
-                }
-            }
-        }
-        else
-            bOk = true;
-
+        bool const bPasswdProvided(aEncryptionData.getLength() != 0);
         pFilter = rMedium.GetFilter();
 
         const SfxStringItem *pVersionItem = !rMedium.IsInCheckIn()? 
SfxItemSet::GetItem<SfxStringItem>(pSet, SID_DOCINFO_COMMENTS, false): nullptr;
@@ -1777,22 +1744,10 @@ bool SfxObjectShell::SaveTo_Impl
             // now create the outer storage
             uno::Reference<embed::XStorage> const 
xOuterStorage(rMedium.GetOutputStorage());
             assert(xOuterStorage.is());
+            assert(!rMedium.GetErrorCode());
             // the outer storage needs the same properties as the inner one
             SetupStorage(xOuterStorage, SOFFICE_FILEFORMAT_CURRENT, false);
 
-#if 0
-            // does this need to happen here? - GetStorage already did it
-            try {
-                
::comphelper::OStorageHelper::SetCommonStorageEncryptionData(xOuterStorage, 
aEncryptionData);
-            }
-            catch (uno::Exception&)
-            {
-                SAL_WARN("sfx.doc", "Setting of common encryption key 
failed!");
-                SetError(ERRCODE_IO_GENERAL);
-                bOk = false;
-            }
-#endif
-
             uno::Reference<io::XStream> const xEncryptedInnerPackage =
                 xOuterStorage->openStreamElement(
                     "encrypted-package", embed::ElementModes::WRITE);
@@ -3310,6 +3265,9 @@ bool SfxObjectShell::LoadOwnFormat( SfxMedium& rMedium )
         const SfxStringItem* pPasswdItem = 
rMedium.GetItemSet().GetItem(SID_PASSWORD, false);
         if ( pPasswdItem || ERRCODE_IO_ABORT != CheckPasswd_Impl( this, 
pMedium ) )
         {
+            // note: this could be needed in case no interaction handler is
+            // provided (which CheckPasswd_Impl needs) but a password item is,
+            // but it could be done in a better way
             uno::Sequence< beans::NamedValue > aEncryptionData;
             if ( GetEncryptionData_Impl(&pMedium->GetItemSet(), 
aEncryptionData) )
             {
commit 3b347664b26d58d44f685a607a5e6d10dff89cd4
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Wed Dec 6 15:28:14 2023 +0100
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Thu Dec 7 09:28:38 2023 +0100

    tdf#105844 package,sfx2: wholesome ODF package wrapping encryption
    
    Redo the ODF encryption by storing an ODF package and wrapping it as a
    stream "encrypted-package" in another ODF package, such that there is
    only one encrypted stream - this requires only one KDF computation.
    
    * This is only enabled in Experimental mode for now.
    
    * Avoid storing unencrypted data in the pTempFile of SfxMedium, as it
      is usually created in the same directory as the target file, which
      may be on a network share or similar less trusted location.
    
    * SfxMedium::SetEncryptionDataToStorage_Impl() should just set an
      error status if it fails (how can it fail anyway)
    
    * when loading a document, SfxDocPasswordVerifier extracts an encrypted
      inner package (by calling SfxMedium::TryEncryptedInnerPackage())
    
    * SfxMedium::GetStorage() automatically decrypts an encrypted inner
      storage and sets it as the SfxMedium's xStorage
    
    * when storing a document, SfxObjectShell::SaveTo_Impl() creates
      the wrapped storages
    
    * One challenge is to keep the macro/scripting signature working; this
      can only be put in the inner storage, whereas the document signature
      should continue to be on the outer storage; also it must use a Zip
      storage, to see the "META-INF" directory.  This needs a new
      SfxMedium::GetScriptingStorageToSign_Impl() and changes in
      SfxMedium::SignContents_Impl().
    
    Change-Id: Ibfee36ce3a9cd030f2aa2ce1484b6d001cba2389
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160401
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/include/sfx2/docfile.hxx b/include/sfx2/docfile.hxx
index 377bab62830f..fb8039f6b8f1 100644
--- a/include/sfx2/docfile.hxx
+++ b/include/sfx2/docfile.hxx
@@ -70,7 +70,7 @@ class SFX2_DLLPUBLIC SfxMedium final : public SvRefBase
     SAL_DLLPRIVATE void CloseOutStream_Impl();
     SAL_DLLPRIVATE void CloseStreams_Impl(bool bInDestruction = false);
 
-    SAL_DLLPRIVATE void SetEncryptionDataToStorage_Impl();
+    SAL_DLLPRIVATE bool SetEncryptionDataToStorage_Impl();
 
 public:
 
@@ -218,10 +218,14 @@ public:
     SAL_DLLPRIVATE OUString const & GetBackup_Impl();
 
     SAL_DLLPRIVATE css::uno::Reference< css::embed::XStorage > const & 
GetZipStorageToSign_Impl( bool bReadOnly = true );
+    SAL_DLLPRIVATE css::uno::Reference<css::embed::XStorage> 
GetScriptingStorageToSign_Impl();
     SAL_DLLPRIVATE void CloseZipStorage_Impl();
 
     // the storage that will be returned by the medium on GetStorage request
     SAL_DLLPRIVATE void SetStorage_Impl( const css::uno::Reference< 
css::embed::XStorage >& xNewStorage );
+    SAL_DLLPRIVATE void SetInnerStorage_Impl(const 
css::uno::Reference<css::embed::XStorage>& xStorage);
+    SAL_DLLPRIVATE css::uno::Reference<css::embed::XStorage>
+        TryEncryptedInnerPackage(css::uno::Reference<css::embed::XStorage> 
xStorage);
 
     SAL_DLLPRIVATE void CloseAndReleaseStreams_Impl();
     SAL_DLLPRIVATE void AddVersion_Impl( css::util::RevisionTag& rVersion );
diff --git a/package/source/zippackage/ZipPackage.cxx 
b/package/source/zippackage/ZipPackage.cxx
index 4dc2021a1904..46e87f437c7b 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -287,7 +287,9 @@ void ZipPackage::parseManifest()
                                     const sal_Int32 nStartKeyAlg = 
xml::crypto::DigestID::SHA256;
                                     pStream->SetImportedStartKeyAlgorithm( 
nStartKeyAlg );
 
-                                    if ( !m_bHasEncryptedEntries && 
pStream->getName() == "content.xml" )
+                                    if (!m_bHasEncryptedEntries
+                                        && (pStream->getName() == "content.xml"
+                                            || pStream->getName() == 
"encrypted-package"))
                                     {
                                         m_bHasEncryptedEntries = true;
                                         m_nChecksumDigestID = nDigestAlg;
@@ -336,7 +338,9 @@ void ZipPackage::parseManifest()
                                     pStream->SetToBeCompressed ( true );
                                     pStream->SetToBeEncrypted ( true );
                                     pStream->SetIsEncrypted ( true );
-                                    if ( !m_bHasEncryptedEntries && 
pStream->getName() == "content.xml" )
+                                    if (!m_bHasEncryptedEntries
+                                        && (pStream->getName() == "content.xml"
+                                            || pStream->getName() == 
"encrypted-package"))
                                     {
                                         m_bHasEncryptedEntries = true;
                                         m_nStartKeyGenerationID = nStartKeyAlg;
diff --git a/sfx2/source/appl/appopen.cxx b/sfx2/source/appl/appopen.cxx
index 8e069c14e256..9693a5f74b04 100644
--- a/sfx2/source/appl/appopen.cxx
+++ b/sfx2/source/appl/appopen.cxx
@@ -115,16 +115,19 @@ namespace {
 class SfxDocPasswordVerifier : public ::comphelper::IDocPasswordVerifier
 {
 public:
-    explicit     SfxDocPasswordVerifier( const Reference< embed::XStorage >& 
rxStorage ) :
-                            mxStorage( rxStorage ) {}
+    explicit SfxDocPasswordVerifier(SfxMedium& rMedium)
+        : m_rMedium(rMedium)
+        , mxStorage(rMedium.GetStorage())
+    {
+    }
 
     virtual ::comphelper::DocPasswordVerifierResult
                         verifyPassword( const OUString& rPassword, 
uno::Sequence< beans::NamedValue >& o_rEncryptionData ) override;
     virtual ::comphelper::DocPasswordVerifierResult
                         verifyEncryptionData( const uno::Sequence< 
beans::NamedValue >& rEncryptionData ) override;
 
-
 private:
+    SfxMedium & m_rMedium;
     Reference< embed::XStorage > mxStorage;
 };
 
@@ -147,9 +150,14 @@ private:
         // and immediately closed
         ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( 
mxStorage, rEncryptionData );
 
-        mxStorage->openStreamElement(
+        // for new ODF encryption, try to extract the encrypted inner package
+        // (it will become the SfxObjectShell storage)
+        if (!m_rMedium.TryEncryptedInnerPackage(mxStorage))
+        {   // ... old ODF encryption:
+            mxStorage->openStreamElement(
                 "content.xml",
                 embed::ElementModes::READ | embed::ElementModes::NOCREATE );
+        }
 
         // no exception -> success
         eResult = ::comphelper::DocPasswordVerifierResult::OK;
@@ -247,7 +255,7 @@ ErrCode CheckPasswd_Impl
                                                      { "ForSalvage", 
css::uno::Any(true) } });
                         }
 
-                        SfxDocPasswordVerifier aVerifier( xStorage );
+                        SfxDocPasswordVerifier aVerifier(*pFile);
                         aEncryptionData = 
::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
                             aVerifier, aEncryptionData, aPassword, 
xInteractionHandler, pFile->GetOrigURL(), 
comphelper::DocPasswordRequestType::Standard );
 
diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx
index f4dd7b62e10f..c801fb52bc41 100644
--- a/sfx2/source/doc/docfile.cxx
+++ b/sfx2/source/doc/docfile.cxx
@@ -62,9 +62,11 @@
 #include <com/sun/star/io/XInputStream.hpp>
 #include <com/sun/star/io/XTruncate.hpp>
 #include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/TempFile.hpp>
 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
 #include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
 #include <com/sun/star/beans/NamedValue.hpp>
 #include <com/sun/star/beans/PropertyValue.hpp>
 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
@@ -374,6 +376,8 @@ public:
     bool m_bInCheckIn:1;
     bool m_bDisableFileSync = false;
     bool m_bNotifyWhenEditable = false;
+    /// if true, xStorage is an inner package and not directly from xStream
+    bool m_bODFWholesomeEncryption = false;
 
     OUString m_aName;
     OUString m_aLogicName;
@@ -410,6 +414,9 @@ public:
     uno::Reference<io::XStream> xStream;
     uno::Reference<io::XStream> m_xLockingStream;
     uno::Reference<task::XInteractionHandler> xInteraction;
+    uno::Reference<io::XStream> m_xODFDecryptedInnerPackageStream;
+    uno::Reference<embed::XStorage> m_xODFEncryptedOuterStorage;
+    uno::Reference<embed::XStorage> m_xODFDecryptedInnerZipStorage;
 
     ErrCodeMsg  nLastStorageError;
 
@@ -670,6 +677,7 @@ bool SfxMedium::IsSkipImages() const
 
 SvStream* SfxMedium::GetInStream()
 {
+    //assert(!pImpl->xStorage); // either SvStream or Storage
     if ( pImpl->m_pInStream )
         return pImpl->m_pInStream.get();
 
@@ -740,6 +748,7 @@ void SfxMedium::CloseInStream_Impl(bool bInDestruction)
 
 SvStream* SfxMedium::GetOutStream()
 {
+    assert(!pImpl->xStorage); // either SvStream or Storage
     if ( !pImpl->m_pOutStream )
     {
         // Create a temp. file if there is none because we always
@@ -953,8 +962,11 @@ uno::Reference < embed::XStorage > 
SfxMedium::GetOutputStorage()
 
     // if the medium was constructed with a Storage: use this one, not a temp. 
storage
     // if a temporary storage already exists: use it
-    if ( pImpl->xStorage.is() && ( pImpl->m_aLogicName.isEmpty() || 
pImpl->pTempFile ) )
+    if (pImpl->xStorage.is()
+        && (pImpl->m_bODFWholesomeEncryption || pImpl->m_aLogicName.isEmpty() 
|| pImpl->pTempFile))
+    {
         return pImpl->xStorage;
+    }
 
     // if necessary close stream that was used for reading
     if ( pImpl->m_pInStream && !pImpl->m_pInStream->IsWritable() )
@@ -971,15 +983,15 @@ uno::Reference < embed::XStorage > 
SfxMedium::GetOutputStorage()
 }
 
 
-void SfxMedium::SetEncryptionDataToStorage_Impl()
+bool SfxMedium::SetEncryptionDataToStorage_Impl()
 {
     // in case media-descriptor contains password it should be used on opening
     if ( !pImpl->xStorage.is() || !pImpl->m_pSet )
-        return;
+        return false;
 
     uno::Sequence< beans::NamedValue > aEncryptionData;
     if ( !GetEncryptionData_Impl( pImpl->m_pSet.get(), aEncryptionData ) )
-        return;
+        return false;
 
     // replace the password with encryption data
     pImpl->m_pSet->ClearItem( SID_PASSWORD );
@@ -992,9 +1004,10 @@ void SfxMedium::SetEncryptionDataToStorage_Impl()
     catch( const uno::Exception& )
     {
         SAL_WARN( "sfx.doc", "It must be possible to set a common password for 
the storage" );
-        // TODO/LATER: set the error code in case of problem
-        // SetError(ERRCODE_IO_GENERAL);
+        SetError(ERRCODE_IO_GENERAL);
+        return false;
     }
+    return true;
 }
 
 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
@@ -1699,12 +1712,54 @@ SfxMedium::LockFileResult 
SfxMedium::LockOrigFileOnDemand(bool bLoading, bool bN
 #endif
 }
 
+// this either returns non-null or throws exception
+uno::Reference<embed::XStorage>
+SfxMedium::TryEncryptedInnerPackage(uno::Reference<embed::XStorage> const 
xStorage)
+{
+    uno::Reference<embed::XStorage> xRet;
+    if (xStorage->hasByName("encrypted-package"))
+    {
+        uno::Reference<io::XStream> const
+            xDecryptedInnerPackage = xStorage->openStreamElement(
+                "encrypted-package",
+                embed::ElementModes::READ | embed::ElementModes::NOCREATE);
+        assert(xDecryptedInnerPackage.is()); // just for testing? not if wrong 
pwd
+        // need a seekable stream => copy
+        Reference<uno::XComponentContext> const 
xContext(::comphelper::getProcessComponentContext());
+        uno::Reference<io::XStream> const xDecryptedInnerPackageStream(
+            xContext->getServiceManager()->createInstanceWithContext(
+                "com.sun.star.comp.MemoryStream", xContext),
+            UNO_QUERY_THROW);
+        
comphelper::OStorageHelper::CopyInputToOutput(xDecryptedInnerPackage->getInputStream(),
 xDecryptedInnerPackageStream->getOutputStream());
+        xDecryptedInnerPackageStream->getOutputStream()->closeOutput();
+#if 0
+        // debug: dump to temp file
+        uno::Reference<io::XTempFile> const 
xTempFile(io::TempFile::create(xContext), uno::UNO_SET_THROW);
+        xTempFile->setRemoveFile(false);
+        
comphelper::OStorageHelper::CopyInputToOutput(xDecryptedInnerPackageStream->getInputStream(),
 xTempFile->getOutputStream());
+        xTempFile->getOutputStream()->closeOutput();
+        SAL_DE BUG("AAA tempfile " << xTempFile->getResourceName());
+        uno::Reference<io::XSeekable>(xDecryptedInnerPackageStream, 
uno::UNO_QUERY_THROW)->seek(0);
+#endif
+        // create storage, if this succeeds assume password is correct
+        xRet = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+            PACKAGE_STORAGE_FORMAT_STRING, xDecryptedInnerPackageStream,
+            embed::ElementModes::READWRITE, xContext, false);
+        assert(xRet.is());
+        pImpl->m_bODFWholesomeEncryption = true;
+        pImpl->m_xODFDecryptedInnerPackageStream = 
xDecryptedInnerPackageStream;
+        pImpl->m_xODFEncryptedOuterStorage = xStorage;
+        pImpl->xStorage = xRet;
+    }
+    return xRet;
+}
 
 uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile 
)
 {
     if ( pImpl->xStorage.is() || pImpl->m_bTriedStorage )
         return pImpl->xStorage;
 
+    assert(!pImpl->m_pOutStream /*&& !pImpl->m_pInStream*/); // either 
SvStream or Storage
     uno::Sequence< uno::Any > aArgs( 2 );
     auto pArgs = aArgs.getArray();
 
@@ -1797,10 +1852,26 @@ uno::Reference < embed::XStorage > 
SfxMedium::GetStorage( bool bCreateTempFile )
 
     pImpl->m_bTriedStorage = true;
 
+    if (pImpl->xStorage.is())
+    {
+        pImpl->m_bODFWholesomeEncryption = false;
+        if (SetEncryptionDataToStorage_Impl())
+        {
+            try
+            {
+                TryEncryptedInnerPackage(pImpl->xStorage);
+            }
+            catch (Exception const&)
+            {
+                TOOLS_WARN_EXCEPTION("sfx.doc", "exception from 
TryEncryptedInnerPackage: ");
+                SetError(ERRCODE_IO_GENERAL);
+            }
+        }
+    }
+
     // TODO/LATER: Get versionlist on demand
     if ( pImpl->xStorage.is() )
     {
-        SetEncryptionDataToStorage_Impl();
         GetVersionList();
     }
 
@@ -1867,6 +1938,8 @@ uno::Reference < embed::XStorage > SfxMedium::GetStorage( 
bool bCreateTempFile )
     if ( bResetStorage )
     {
         pImpl->xStorage.clear();
+        pImpl->m_xODFDecryptedInnerPackageStream.clear();
+        pImpl->m_xODFEncryptedOuterStorage.clear();
         if ( pImpl->m_pInStream )
             pImpl->m_pInStream->Seek( 0 );
     }
@@ -1875,7 +1948,39 @@ uno::Reference < embed::XStorage > 
SfxMedium::GetStorage( bool bCreateTempFile )
     return pImpl->xStorage;
 }
 
+uno::Reference<embed::XStorage> SfxMedium::GetScriptingStorageToSign_Impl()
+{
+    // this was set when it was initially loaded
+    if (pImpl->m_bODFWholesomeEncryption)
+    {
+        // (partial) scripting signature can only be in inner storage!
+        // Note: a "PackageFormat" storage like pImpl->xStorage doesn't work
+        // (even if it's not encrypted) because it hides the "META-INF" dir.
+        // This "ZipFormat" storage is used only read-only; a writable one is
+        // created manually in SignContents_Impl().
+        if (!pImpl->m_xODFDecryptedInnerZipStorage.is())
+        {
+            GetStorage(false);
+            // don't care about xStorage here because Zip is readonly
+            SAL_WARN_IF(!pImpl->m_xODFDecryptedInnerPackageStream.is(), 
"sfx.doc", "no inner package stream?");
+            if (pImpl->m_xODFDecryptedInnerPackageStream.is())
+            {
+                pImpl->m_xODFDecryptedInnerZipStorage =
+                    
::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream(
+                        ZIP_STORAGE_FORMAT_STRING,
+                        
pImpl->m_xODFDecryptedInnerPackageStream->getInputStream());
+            }
+        }
+        return pImpl->m_xODFDecryptedInnerZipStorage;
+    }
+    else
+    {
+        return GetZipStorageToSign_Impl(true);
+    }
+}
 
+// note: currently nobody who calls this with "false" writes into an ODF
+// storage that is returned here, that is only for OOXML
 uno::Reference< embed::XStorage > const & SfxMedium::GetZipStorageToSign_Impl( 
bool bReadOnly )
 {
     if ( !GetErrorIgnoreWarning() && !pImpl->m_xZipStorage.is() )
@@ -1919,6 +2024,7 @@ void SfxMedium::CloseZipStorage_Impl()
 
         pImpl->m_xZipStorage.clear();
     }
+    pImpl->m_xODFDecryptedInnerZipStorage.clear();
 }
 
 void SfxMedium::CloseStorage()
@@ -1938,6 +2044,9 @@ void SfxMedium::CloseStorage()
         }
 
         pImpl->xStorage.clear();
+        pImpl->m_xODFDecryptedInnerPackageStream.clear();
+//        pImpl->m_xODFDecryptedInnerZipStorage.clear();
+        pImpl->m_xODFEncryptedOuterStorage.clear();
         pImpl->bStorageBasedOnInStream = false;
     }
 
@@ -3632,12 +3741,17 @@ void SfxMedium::SetLoadTargetFrame(SfxFrame* pFrame )
     pImpl->wLoadTargetFrame = pFrame;
 }
 
-
-void SfxMedium::SetStorage_Impl( const uno::Reference < embed::XStorage >& 
rStor )
+void SfxMedium::SetStorage_Impl(const uno::Reference<embed::XStorage>& 
xStorage)
 {
-    pImpl->xStorage = rStor;
+    pImpl->xStorage = xStorage;
+    pImpl->m_bODFWholesomeEncryption = false;
 }
 
+void SfxMedium::SetInnerStorage_Impl(const uno::Reference<embed::XStorage>& 
xStorage)
+{
+    pImpl->xStorage = xStorage;
+    pImpl->m_bODFWholesomeEncryption = true;
+}
 
 SfxItemSet& SfxMedium::GetItemSet() const
 {
@@ -4173,7 +4287,18 @@ bool SfxMedium::SignContents_Impl(weld::Window* 
pDialogParent,
         bool bODF = GetFilter()->IsOwnFormat();
         try
         {
-            xWriteableZipStor = 
::comphelper::OStorageHelper::GetStorageOfFormatFromStream( 
ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
+            if (pImpl->m_bODFWholesomeEncryption && bSignScriptingContent)
+            {
+                assert(pImpl->xStorage); // GetStorage was called above
+                assert(pImpl->m_xODFDecryptedInnerPackageStream);
+                xWriteableZipStor = 
::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+                    ZIP_STORAGE_FORMAT_STRING, 
pImpl->m_xODFDecryptedInnerPackageStream);
+            }
+            else
+            {
+                xWriteableZipStor = 
::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+                    ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
+            }
         }
         catch (const io::IOException&)
         {
@@ -4205,7 +4330,11 @@ bool SfxMedium::SignContents_Impl(weld::Window* 
pDialogParent,
                                                 embed::ElementModes::READWRITE 
),
                 uno::UNO_SET_THROW );
 
-            if ( xSigner->signScriptingContent( GetZipStorageToSign_Impl(), 
xStream ) )
+            // note: the storage passed here must be independent from the
+            // xWriteableZipStor because a writable storage can't have 2
+            // instances of sub-storage for the same directory open, but with
+            // independent storages it somehow works
+            if 
(xSigner->signScriptingContent(GetScriptingStorageToSign_Impl(), xStream))
             {
                 // remove the document signature if any
                 OUString aDocSigName = 
xSigner->getDocumentContentSignatureDefaultStreamName();
@@ -4217,6 +4346,20 @@ bool SfxMedium::SignContents_Impl(weld::Window* 
pDialogParent,
                 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
                 xTransact->commit();
 
+                if (pImpl->m_bODFWholesomeEncryption)
+                {   // manually copy the inner package to the outer one
+                    
uno::Reference<io::XSeekable>(pImpl->m_xODFDecryptedInnerPackageStream, 
uno::UNO_QUERY_THROW)->seek(0);
+                    uno::Reference<io::XStream> const xEncryptedPackage =
+                        pImpl->m_xODFEncryptedOuterStorage->openStreamElement(
+                            "encrypted-package",
+                            
embed::ElementModes::WRITE|embed::ElementModes::TRUNCATE);
+                    
comphelper::OStorageHelper::CopyInputToOutput(pImpl->m_xODFDecryptedInnerPackageStream->getInputStream(),
 xEncryptedPackage->getOutputStream());
+                    xTransact.set(pImpl->m_xODFEncryptedOuterStorage, 
uno::UNO_QUERY_THROW);
+                    xTransact->commit(); // Commit() below won't do this
+                }
+
+                assert(!pImpl->xStorage.is() // ensure this doesn't overwrite
+                    || !uno::Reference<util::XModifiable>(pImpl->xStorage, 
uno::UNO_QUERY_THROW)->isModified());
                 // the temporary file has been written, commit it to the 
original file
                 Commit();
                 bChanges = true;
diff --git a/sfx2/source/doc/objmisc.cxx b/sfx2/source/doc/objmisc.cxx
index ede8983608c6..5b7aa98df38e 100644
--- a/sfx2/source/doc/objmisc.cxx
+++ b/sfx2/source/doc/objmisc.cxx
@@ -1924,7 +1924,7 @@ bool SfxObjectShell_Impl::hasTrustedScriptingSignature(
                     {
                         task::DocumentMacroConfirmationRequest aRequest;
                         aRequest.DocumentURL = getDocumentLocation();
-                        aRequest.DocumentStorage = 
rDocShell.GetMedium()->GetZipStorageToSign_Impl();
+                        aRequest.DocumentStorage = 
rDocShell.GetMedium()->GetScriptingStorageToSign_Impl();
                         aRequest.DocumentSignatureInformation = aInfo;
                         aRequest.DocumentVersion = aVersion;
                         aRequest.Classification = 
task::InteractionClassification_QUERY;
diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx
index fcdc66f139b5..5372807fba67 100644
--- a/sfx2/source/doc/objserv.cxx
+++ b/sfx2/source/doc/objserv.cxx
@@ -1840,8 +1840,11 @@ uno::Sequence< security::DocumentSignatureInformation > 
SfxObjectShell::GetDocum
             }
 
             if ( bScriptingContent )
-                aResult = xLocSigner->verifyScriptingContentSignatures( 
GetMedium()->GetZipStorageToSign_Impl(),
-                                                                
uno::Reference< io::XInputStream >() );
+            {
+                aResult = xLocSigner->verifyScriptingContentSignatures(
+                    GetMedium()->GetScriptingStorageToSign_Impl(),
+                    uno::Reference<io::XInputStream>());
+            }
             else
             {
                 if (GetMedium()->GetStorage(false).is())
@@ -2034,7 +2037,7 @@ bool SfxObjectShell::CheckIsReadonly(bool 
bSignScriptingContent, weld::Window* p
                 xSigner->setParentWindow(pDialogParent->GetXWindow());
 
             if (bSignScriptingContent)
-                
xSigner->showScriptingContentSignatures(GetMedium()->GetZipStorageToSign_Impl(),
+                
xSigner->showScriptingContentSignatures(GetMedium()->GetScriptingStorageToSign_Impl(),
                                                         
uno::Reference<io::XInputStream>());
             else
             {
diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx
index b39b5d14ee38..9ae2c8faad63 100644
--- a/sfx2/source/doc/objstor.cxx
+++ b/sfx2/source/doc/objstor.cxx
@@ -1150,6 +1150,9 @@ bool SfxObjectShell::SaveTo_Impl
     // tdf#41063, tdf#135244: prevent jumping to cursor at any temporary 
modification
     auto aViewGuard(LockAllViews());
 
+    uno::Reference<uno::XComponentContext> const xContext(
+        ::comphelper::getProcessComponentContext());
+
     std::shared_ptr<const SfxFilter> pFilter = rMedium.GetFilter();
     if ( !pFilter )
     {
@@ -1173,6 +1176,12 @@ bool SfxObjectShell::SaveTo_Impl
         return false;
     }
 
+    SvtSaveOptions::ODFSaneDefaultVersion 
nVersion(SvtSaveOptions::ODFSVER_LATEST_EXTENDED);
+    if (bOwnTarget && !utl::ConfigManager::IsFuzzing())
+    {
+        nVersion = GetODFSaneDefaultVersion();
+    }
+
     bool bNeedsDisconnectionOnFail = false;
 
     bool bStoreToSameLocation = false;
@@ -1193,7 +1202,6 @@ bool SfxObjectShell::SaveTo_Impl
         if ( bTryToPreserveScriptSignature )
         {
             // check that the storage format stays the same
-            SvtSaveOptions::ODFSaneDefaultVersion nVersion = 
GetODFSaneDefaultVersion();
 
             OUString aODFVersion;
             try
@@ -1220,6 +1228,33 @@ bool SfxObjectShell::SaveTo_Impl
         }
     }
 
+    uno::Reference<io::XStream> xODFDecryptedInnerPackageStream;
+    uno::Reference<embed::XStorage> xODFDecryptedInnerPackage;
+    uno::Sequence<beans::NamedValue> aEncryptionData;
+    if (GetEncryptionData_Impl(&rMedium.GetItemSet(), aEncryptionData))
+    {
+        assert(aEncryptionData.getLength() != 0);
+        if (bOwnTarget && nVersion == SvtSaveOptions::ODFSVER_LATEST_EXTENDED
+            && officecfg::Office::Common::Misc::ExperimentalMode::get())
+        {
+            // when embedded objects are stored here, it should be called from
+            // this function for the root document and encryption data was 
cleared
+            assert(GetCreateMode() != SfxObjectCreateMode::EMBEDDED);
+            // clear now to store inner package (+ embedded objects) 
unencrypted
+            rMedium.GetItemSet().ClearItem(SID_ENCRYPTIONDATA);
+            rMedium.GetItemSet().ClearItem(SID_PASSWORD);
+            xODFDecryptedInnerPackageStream.set(
+                xContext->getServiceManager()->createInstanceWithContext(
+                    "com.sun.star.comp.MemoryStream", xContext),
+                UNO_QUERY_THROW);
+            xODFDecryptedInnerPackage = 
::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+                PACKAGE_STORAGE_FORMAT_STRING, xODFDecryptedInnerPackageStream,
+                css::embed::ElementModes::WRITE, xContext, false);
+            assert(xODFDecryptedInnerPackage.is());
+        }
+    }
+
+    bool isStreamAndInputStreamCleared(false);
     // use UCB for case sensitive/insensitive file name comparison
     if ( !pMedium->GetName().equalsIgnoreAsciiCase("private:stream")
       && !rMedium.GetName().equalsIgnoreAsciiCase("private:stream")
@@ -1280,6 +1315,7 @@ bool SfxObjectShell::SaveTo_Impl
               || ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
             {
                 pMedium->CloseAndRelease();
+                isStreamAndInputStreamCleared = true;
 
                 // TODO/LATER: for now the medium must be closed since it can 
already contain streams from old medium
                 //             in future those streams should not be copied in 
case a valid target url is provided,
@@ -1287,7 +1323,15 @@ bool SfxObjectShell::SaveTo_Impl
                 //             reachable.
                 rMedium.CloseAndRelease();
                 
rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects());
-                rMedium.GetOutputStorage();
+                if (xODFDecryptedInnerPackageStream.is())
+                {
+                    assert(!rMedium.GetItemSet().GetItem(SID_STREAM));
+                    rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
+                }
+                else
+                {
+                    rMedium.GetOutputStorage();
+                }
                 rMedium.SetHasEmbeddedObjects(false);
             }
         }
@@ -1299,6 +1343,7 @@ bool SfxObjectShell::SaveTo_Impl
 
             pMedium->CloseAndRelease();
             rMedium.CloseAndRelease();
+            isStreamAndInputStreamCleared = true;
             rMedium.CreateTempFileNoCopy();
             rMedium.GetOutStream();
         }
@@ -1310,7 +1355,16 @@ bool SfxObjectShell::SaveTo_Impl
 
             pMedium->CloseAndRelease();
             rMedium.CloseAndRelease();
-            rMedium.GetOutputStorage();
+            isStreamAndInputStreamCleared = true;
+            if (xODFDecryptedInnerPackageStream.is())
+            {
+                assert(!rMedium.GetItemSet().GetItem(SID_STREAM));
+                rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
+            }
+            else
+            {
+                rMedium.GetOutputStorage();
+            }
         }
         else // means if ( bStorageBasedSource && !bStorageBasedTarget )
         {
@@ -1325,6 +1379,7 @@ bool SfxObjectShell::SaveTo_Impl
             {
                 pMedium->CloseAndRelease();
                 rMedium.CloseAndRelease();
+                isStreamAndInputStreamCleared = true;
                 rMedium.CreateTempFileNoCopy();
                 rMedium.GetOutStream();
             }
@@ -1339,10 +1394,20 @@ bool SfxObjectShell::SaveTo_Impl
         // TODO/LATER: let the medium be prepared for alien formats as well
 
         rMedium.CloseAndRelease();
+        isStreamAndInputStreamCleared = true;
         if ( bStorageBasedTarget )
         {
             
rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects());
-            rMedium.GetOutputStorage();
+            if (xODFDecryptedInnerPackageStream.is())
+            {
+                assert(!rMedium.GetItemSet().GetItem(SID_STREAM));
+                // this should set only xStorage, all of the streams remain 
null
+                rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
+            }
+            else
+            {
+                rMedium.GetOutputStorage();
+            }
             rMedium.SetHasEmbeddedObjects(false);
         }
     }
@@ -1354,6 +1419,9 @@ bool SfxObjectShell::SaveTo_Impl
         return false;
     }
 
+    // these have been cleared on all paths that don't take above error return
+    assert(isStreamAndInputStreamCleared); (void) 
isStreamAndInputStreamCleared;
+
     rMedium.LockOrigFileOnDemand( false, false );
 
     if ( bStorageBasedTarget )
@@ -1416,19 +1484,26 @@ bool SfxObjectShell::SaveTo_Impl
         }
 
         // transfer password from the parameters to the storage
-        uno::Sequence< beans::NamedValue > aEncryptionData;
         bool bPasswdProvided = false;
-        if ( GetEncryptionData_Impl( &rMedSet, aEncryptionData ) )
+        if (aEncryptionData.getLength() != 0)
         {
             bPasswdProvided = true;
-            try {
-                ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( 
xMedStorage, aEncryptionData );
+            if (xODFDecryptedInnerPackageStream.is())
+            {
                 bOk = true;
             }
-            catch( uno::Exception& )
+            else
             {
-                SAL_WARN( "sfx.doc", "Setting of common encryption key 
failed!" );
-                SetError(ERRCODE_IO_GENERAL);
+                // TODO: GetStorage() already did that?
+                try {
+                    
::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xMedStorage, 
aEncryptionData );
+                    bOk = true;
+                }
+                catch( uno::Exception& )
+                {
+                    SAL_WARN( "sfx.doc", "Setting of common encryption key 
failed!" );
+                    SetError(ERRCODE_IO_GENERAL);
+                }
             }
         }
         else
@@ -1576,6 +1651,13 @@ bool SfxObjectShell::SaveTo_Impl
 
     if ( bOk )
     {
+        uno::Any mediaType;
+        if (xODFDecryptedInnerPackageStream.is())
+        {   // before the signature copy closes it
+            mediaType = 
uno::Reference<beans::XPropertySet>(xODFDecryptedInnerPackage,
+                uno::UNO_QUERY_THROW)->getPropertyValue("MediaType");
+        }
+
         // if ODF version of oasis format changes on saving the signature 
should not be preserved
         if ( bTryToPreserveScriptSignature && bNoPreserveForOasis )
             bTryToPreserveScriptSignature = ( SotStorage::GetVersion( 
rMedium.GetStorage() ) == SOFFICE_FILEFORMAT_60 );
@@ -1610,14 +1692,26 @@ bool SfxObjectShell::SaveTo_Impl
                     rMedium.StorageCommit_Impl();
                     rMedium.CloseStorage();
 
-                    uno::Reference< embed::XStorage > xReadOrig = 
pMedium->GetZipStorageToSign_Impl();
+                    // signature must use Zip storage, not Package storage
+                    uno::Reference<embed::XStorage> const xReadOrig(
+                            pMedium->GetScriptingStorageToSign_Impl());
+                    uno::Reference<embed::XStorage> xTarget;
+                    if (xODFDecryptedInnerPackageStream.is())
+                    {
+                        xTarget = 
::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+                            ZIP_STORAGE_FORMAT_STRING, 
xODFDecryptedInnerPackageStream);
+                    }
+                    else
+                    {
+                        xTarget = rMedium.GetZipStorageToSign_Impl(false);
+                    }
+
                     if ( !xReadOrig.is() )
                         throw uno::RuntimeException();
                     uno::Reference< embed::XStorage > xMetaInf = 
xReadOrig->openStorageElement(
                                 "META-INF",
                                 embed::ElementModes::READ );
 
-                    uno::Reference< embed::XStorage > xTarget = 
rMedium.GetZipStorageToSign_Impl( false );
                     if ( !xTarget.is() )
                         throw uno::RuntimeException();
                     uno::Reference< embed::XStorage > xTargetMetaInf = 
xTarget->openStorageElement(
@@ -1648,6 +1742,12 @@ bool SfxObjectShell::SaveTo_Impl
                             xTransact.set( xTarget, uno::UNO_QUERY );
                             if ( xTransact.is() )
                                 xTransact->commit();
+                            if (xODFDecryptedInnerPackageStream.is())
+                            {   // recreate, to have it with copied sig
+                                xODFDecryptedInnerPackage = 
::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+                                    PACKAGE_STORAGE_FORMAT_STRING, 
xODFDecryptedInnerPackageStream,
+                                    css::embed::ElementModes::WRITE, xContext, 
false);
+                            }
                         }
                         else
                         {
@@ -1665,6 +1765,49 @@ bool SfxObjectShell::SaveTo_Impl
             rMedium.CloseZipStorage_Impl();
         }
 
+        if (xODFDecryptedInnerPackageStream.is())
+        {
+            rMedium.StorageCommit_Impl();
+            // prevent dispose as inner storage will be needed later
+            assert(!rMedium.WillDisposeStorageOnClose_Impl());
+            rMedium.CloseStorage();
+            // restore encryption for outer package, note: disable for 
debugging
+            rMedium.GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, 
uno::Any(aEncryptionData)));
+            assert(xODFDecryptedInnerPackageStream.is());
+            // now create the outer storage
+            uno::Reference<embed::XStorage> const 
xOuterStorage(rMedium.GetOutputStorage());
+            assert(xOuterStorage.is());
+            // the outer storage needs the same properties as the inner one
+            SetupStorage(xOuterStorage, SOFFICE_FILEFORMAT_CURRENT, false);
+
+#if 0
+            // does this need to happen here? - GetStorage already did it
+            try {
+                
::comphelper::OStorageHelper::SetCommonStorageEncryptionData(xOuterStorage, 
aEncryptionData);
+            }
+            catch (uno::Exception&)
+            {
+                SAL_WARN("sfx.doc", "Setting of common encryption key 
failed!");
+                SetError(ERRCODE_IO_GENERAL);
+                bOk = false;
+            }
+#endif
+
+            uno::Reference<io::XStream> const xEncryptedInnerPackage =
+                xOuterStorage->openStreamElement(
+                    "encrypted-package", embed::ElementModes::WRITE);
+            uno::Reference<beans::XPropertySet> const xEncryptedPackageProps(
+                    xEncryptedInnerPackage, uno::UNO_QUERY_THROW);
+            xEncryptedPackageProps->setPropertyValue("MediaType", mediaType);
+
+            // encryption: just copy into package stream
+            uno::Reference<io::XSeekable>(xODFDecryptedInnerPackageStream, 
uno::UNO_QUERY_THROW)->seek(0);
+            comphelper::OStorageHelper::CopyInputToOutput(
+                xODFDecryptedInnerPackageStream->getInputStream(),
+                xEncryptedInnerPackage->getOutputStream());
+            // rely on Commit() below
+        }
+
         const OUString sName( rMedium.GetName( ) );
         bOk = rMedium.Commit();
         const OUString sNewName( rMedium.GetName( ) );
@@ -1672,6 +1815,11 @@ bool SfxObjectShell::SaveTo_Impl
         if ( sName != sNewName )
             GetMedium( )->SwitchDocumentToFile( sNewName );
 
+        if (xODFDecryptedInnerPackageStream.is())
+        {   // set the inner storage on the medium again, after Switch
+            rMedium.SetInnerStorage_Impl(xODFDecryptedInnerPackage);
+        }
+
         if ( bOk )
         {
             // if the target medium is an alien format and the "old" medium 
was an own format and the "old" medium

Reply via email to