include/sal/log-areas.dox                        |    1 
 offapi/com/sun/star/xml/crypto/CipherID.idl      |   13 ++
 package/inc/ZipPackage.hxx                       |   14 ++
 package/inc/ZipPackageStream.hxx                 |    2 
 package/source/manifest/ManifestDefines.hxx      |    3 
 package/source/manifest/ManifestExport.cxx       |    9 +
 package/source/manifest/ManifestImport.cxx       |   17 +++
 package/source/zipapi/XUnbufferedStream.cxx      |    1 
 package/source/zipapi/ZipFile.cxx                |    6 -
 package/source/zippackage/ZipPackage.cxx         |   16 ++-
 package/source/zippackage/ZipPackageStream.cxx   |   19 +++
 sfx2/source/doc/objstor.cxx                      |   20 +++
 xmlsecurity/source/xmlsec/nss/ciphercontext.cxx  |  120 ++++++++++++++++++++---
 xmlsecurity/source/xmlsec/nss/ciphercontext.hxx  |    1 
 xmlsecurity/source/xmlsec/nss/nssinitializer.cxx |   17 ++-
 15 files changed, 226 insertions(+), 33 deletions(-)

New commits:
commit f0fda7ad2236f478fea396a23d4f982e5fc37e68
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Fri Dec 8 21:16:31 2023 +0100
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Mon Dec 11 20:46:40 2023 +0100

    tdf#105844 offapi,package,sfx2,xmlsecurity: add AEAD w/ AES GCM
    
    ... and use it in the new experimental ODF encryption mode.
    
    https://www.w3.org/TR/xmlenc-core1/#sec-AES-GCM
    
    Unfortunately it turned out that NSS PK11_CipherOp() does not work with
    CKM_AES_GCM because it is initialized with "context->multi = PR_FALSE"
    in sftk_CryptInit(), so the one-step functions PK11_Encrypt() and
    PK11_Decrypt() have to be used.
    
    NSS 3.52 also changed a parameter struct definition - see
    https://fedoraproject.org/wiki/Changes/NssGCMParams - which is not a
    problem for RHEL or SUSE system NSS since those are rebased, but it
    is likely a problem for less well maintained Ubuntu LTS, so use
    the old struct definition which evidently still works with NSS 3.94.
    
    NSS 3.52 also added a new PK11_AEADOp() API but it looks like this
    doesn't support incremental encryption either.
    
    Change-Id: Ibd4a672db74b65b1218926ba35ff8d2f70444c7e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160505
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 15cba1e538b5..a2240a89a803 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -343,6 +343,7 @@ certain functionality.
 @section package
 
 @li @c package
+@li @c package.manifest
 @li @c package.xstor
 @li @c package.threadeddeflate
 
diff --git a/offapi/com/sun/star/xml/crypto/CipherID.idl 
b/offapi/com/sun/star/xml/crypto/CipherID.idl
index 80d64a995a93..c1ecd297a741 100644
--- a/offapi/com/sun/star/xml/crypto/CipherID.idl
+++ b/offapi/com/sun/star/xml/crypto/CipherID.idl
@@ -30,12 +30,25 @@ module com { module sun { module star { module xml { module 
crypto {
 constants CipherID
 {
     /** identifier of AES algorithm in CBC mode with W3C padding
+
+        @see https://www.w3.org/TR/xmlenc-core1/#sec-Padding
+        @see https://www.w3.org/TR/xmlenc-core1/#sec-AES
      */
     const long AES_CBC_W3C_PADDING = 1;
 
     /** identifier of the Blowfish algorithm in 8-bit CFB mode
      */
     const long BLOWFISH_CFB_8 = 2;
+
+    /** identifier of AES algorithm in GCM mode with 96-bit IV prefixed,
+        128 bit authentication tag, and no padding, as specified in
+        [XMLENC-CORE1] 5.2.4 AES-GCM.
+
+        @see https://www.w3.org/TR/xmlenc-core1/#sec-AES-GCM
+
+        @since LO 24.2
+     */
+    const long AES_GCM_W3C = 3;
 };
 
 
diff --git a/package/inc/ZipPackage.hxx b/package/inc/ZipPackage.hxx
index e83958241247..48067f39b6f7 100644
--- a/package/inc/ZipPackage.hxx
+++ b/package/inc/ZipPackage.hxx
@@ -31,6 +31,7 @@
 #include <com/sun/star/xml/crypto/CipherID.hpp>
 #include <comphelper/refcountedmutex.hxx>
 #include <rtl/ref.hxx>
+#include <o3tl/unreachable.hxx>
 
 #include "HashMaps.hxx"
 #include "ZipFile.hxx"
@@ -124,7 +125,18 @@ public:
     sal_Int32 GetStartKeyGenID() const { return m_nStartKeyGenerationID; }
     sal_Int32 GetEncAlgID() const { return m_nCommonEncryptionID; }
     sal_Int32 GetChecksumAlgID() const { return m_nChecksumDigestID; }
-    sal_Int32 GetDefaultDerivedKeySize() const { return m_nCommonEncryptionID 
== css::xml::crypto::CipherID::AES_CBC_W3C_PADDING ? 32 : 16; }
+    sal_Int32 GetDefaultDerivedKeySize() const {
+        switch (m_nCommonEncryptionID)
+        {
+            case css::xml::crypto::CipherID::BLOWFISH_CFB_8:
+                return 16;
+            case css::xml::crypto::CipherID::AES_CBC_W3C_PADDING:
+            case css::xml::crypto::CipherID::AES_GCM_W3C:
+                return 32;
+            default:
+                O3TL_UNREACHABLE;
+        }
+    }
 
     rtl::Reference<comphelper::RefCountedMutex>& GetSharedMutexRef() { return 
m_aMutexHolder; }
 
diff --git a/package/inc/ZipPackageStream.hxx b/package/inc/ZipPackageStream.hxx
index 194ff729adbe..91beaf276719 100644
--- a/package/inc/ZipPackageStream.hxx
+++ b/package/inc/ZipPackageStream.hxx
@@ -89,7 +89,7 @@ public:
     sal_Int32 GetStartKeyGenID() const;
 
     sal_Int32 GetEncryptionAlgorithm() const;
-    sal_Int32 GetBlockSize() const;
+    sal_Int32 GetIVSize() const;
 
     void SetToBeCompressed (bool bNewValue) { m_bToBeCompressed = bNewValue;}
     void SetIsEncrypted (bool bNewValue) { m_bIsEncrypted = bNewValue;}
diff --git a/package/source/manifest/ManifestDefines.hxx 
b/package/source/manifest/ManifestDefines.hxx
index 48c06a362261..ae2095f5aab5 100644
--- a/package/source/manifest/ManifestDefines.hxx
+++ b/package/source/manifest/ManifestDefines.hxx
@@ -86,6 +86,9 @@ inline constexpr OUString BLOWFISH_URL = 
u"urn:oasis:names:tc:opendocument:xmlns
 inline constexpr OUString AES128_URL = 
u"http://www.w3.org/2001/04/xmlenc#aes128-cbc"_ustr;
 inline constexpr OUString AES192_URL = 
u"http://www.w3.org/2001/04/xmlenc#aes192-cbc"_ustr;
 inline constexpr OUString AES256_URL = 
u"http://www.w3.org/2001/04/xmlenc#aes256-cbc"_ustr;
+inline constexpr OUString AESGCM128_URL = 
u"http://www.w3.org/2009/xmlenc11#aes128-gcm"_ustr;
+inline constexpr OUString AESGCM192_URL = 
u"http://www.w3.org/2009/xmlenc11#aes192-gcm"_ustr;
+inline constexpr OUString AESGCM256_URL = 
u"http://www.w3.org/2009/xmlenc11#aes256-gcm"_ustr;
 
 inline constexpr OUString PBKDF2_NAME = u"PBKDF2"_ustr;
 inline constexpr OUString PGP_NAME = u"PGP"_ustr;
diff --git a/package/source/manifest/ManifestExport.cxx 
b/package/source/manifest/ManifestExport.cxx
index be1d21114c1a..9921b1bad8c8 100644
--- a/package/source/manifest/ManifestExport.cxx
+++ b/package/source/manifest/ManifestExport.cxx
@@ -381,6 +381,15 @@ ManifestExport::ManifestExport( uno::Reference< 
xml::sax::XDocumentHandler > con
 
                 sEncAlgName = sAES256_URL;
             }
+            else if (nEncAlgID == xml::crypto::CipherID::AES_GCM_W3C)
+            {
+                SAL_WARN_IF(nDerivedKeySize != 32, "package.manifest", 
"Unexpected key size is provided!");
+                if (nDerivedKeySize != 32)
+                {
+                    throw uno::RuntimeException(THROW_WHERE "Unexpected key 
size is provided!");
+                }
+                sEncAlgName = AESGCM256_URL;
+            }
             else if ( nEncAlgID == xml::crypto::CipherID::BLOWFISH_CFB_8 )
             {
                 sEncAlgName = sBlowfish_Name;
diff --git a/package/source/manifest/ManifestImport.cxx 
b/package/source/manifest/ManifestImport.cxx
index 195324fdbe2a..b7aa57f99ff1 100644
--- a/package/source/manifest/ManifestImport.cxx
+++ b/package/source/manifest/ManifestImport.cxx
@@ -21,6 +21,7 @@
 #include "ManifestDefines.hxx"
 #include <PackageConstants.hxx>
 #include <osl/diagnose.h>
+#include <sal/log.hxx>
 #include <com/sun/star/xml/sax/XAttributeList.hpp>
 #include <com/sun/star/xml/crypto/DigestID.hpp>
 #include <com/sun/star/xml/crypto/CipherID.hpp>
@@ -184,6 +185,22 @@ void ManifestImport::doAlgorithm(StringHashMap 
&rConvertedAttribs)
     if ( aString == BLOWFISH_NAME || aString == BLOWFISH_URL ) {
         aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
         aSequence[PKG_MNFST_ENCALG].Value <<= 
xml::crypto::CipherID::BLOWFISH_CFB_8;
+    } else if (aString == AESGCM256_URL) {
+        aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
+        aSequence[PKG_MNFST_ENCALG].Value <<= 
xml::crypto::CipherID::AES_GCM_W3C;
+        SAL_INFO_IF(nDerivedKeySize != 0 && nDerivedKeySize != 32, 
"package.manifest", "Unexpected derived key length!");
+        SAL_WARN_IF(nDerivedKeySize != 0 && nDerivedKeySize != 32, 
"package.manifest", "Unexpected derived key length!");
+        nDerivedKeySize = 32;
+    } else if (aString == AESGCM192_URL) {
+        aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
+        aSequence[PKG_MNFST_ENCALG].Value <<= 
xml::crypto::CipherID::AES_GCM_W3C;
+        SAL_INFO_IF(nDerivedKeySize != 0 && nDerivedKeySize != 24, 
"package.manifest", "Unexpected derived key length!");
+        nDerivedKeySize = 24;
+    } else if (aString == AESGCM128_URL) {
+        aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
+        aSequence[PKG_MNFST_ENCALG].Value <<= 
xml::crypto::CipherID::AES_GCM_W3C;
+        SAL_INFO_IF(nDerivedKeySize != 0 && nDerivedKeySize != 16, 
"package.manifest", "Unexpected derived key length!");
+        nDerivedKeySize = 16;
     } else if ( aString == AES256_URL ) {
         aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
         aSequence[PKG_MNFST_ENCALG].Value <<= 
xml::crypto::CipherID::AES_CBC_W3C_PADDING;
diff --git a/package/source/zipapi/XUnbufferedStream.cxx 
b/package/source/zipapi/XUnbufferedStream.cxx
index e3c31d5fca1c..bd2cf4d72d72 100644
--- a/package/source/zipapi/XUnbufferedStream.cxx
+++ b/package/source/zipapi/XUnbufferedStream.cxx
@@ -93,6 +93,7 @@ XUnbufferedStream::XUnbufferedStream(
     if ( bMustDecrypt )
     {
         m_xCipherContext = ZipFile::StaticGetCipher( xContext, rData, false );
+        // this is only relevant when padding is used
         mnBlockSize = ( rData->m_nEncAlg == 
xml::crypto::CipherID::AES_CBC_W3C_PADDING ? 16 : 1 );
     }
 
diff --git a/package/source/zipapi/ZipFile.cxx 
b/package/source/zipapi/ZipFile.cxx
index 59bdcf8de891..f700b2722a71 100644
--- a/package/source/zipapi/ZipFile.cxx
+++ b/package/source/zipapi/ZipFile.cxx
@@ -187,7 +187,8 @@ uno::Reference< xml::crypto::XCipherContext > 
ZipFile::StaticGetCipher( const un
         throw ZipIOException("Can not create derived key!" );
     }
 
-    if ( xEncryptionData->m_nEncAlg == 
xml::crypto::CipherID::AES_CBC_W3C_PADDING )
+    if (xEncryptionData->m_nEncAlg == 
xml::crypto::CipherID::AES_CBC_W3C_PADDING
+        || xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_GCM_W3C)
     {
         uno::Reference< uno::XComponentContext > xContext = xArgContext;
         if ( !xContext.is() )
@@ -450,6 +451,9 @@ void CheckSequence( const uno::Sequence< sal_Int8 >& 
aSequence )
 
 bool ZipFile::StaticHasValidPassword( const uno::Reference< 
uno::XComponentContext >& rxContext, const Sequence< sal_Int8 > &aReadBuffer, 
const ::rtl::Reference< EncryptionData > &rData )
 {
+    if (rData->m_nEncAlg == xml::crypto::CipherID::AES_GCM_W3C)
+        return true; /*TODO fails because of tag*/
+
     if ( !rData.is() || !rData->m_aKey.hasElements() )
         return false;
 
diff --git a/package/source/zippackage/ZipPackage.cxx 
b/package/source/zippackage/ZipPackage.cxx
index 46e87f437c7b..aacb5cd22a45 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -1714,7 +1714,9 @@ void SAL_CALL ZipPackage::setPropertyValue( const 
OUString& aPropertyName, const
                 sal_Int32 nID = 0;
                 if ( !( rAlgorithm.Value >>= nID )
                   || ( nID != xml::crypto::DigestID::SHA256 && nID != 
xml::crypto::DigestID::SHA1 ) )
-                    throw IllegalArgumentException(THROW_WHERE "Unexpected 
start key generation algorithm is provided!", uno::Reference< uno::XInterface 
>(), 2 );
+                {
+                    throw IllegalArgumentException(THROW_WHERE "Unexpected 
start key generation algorithm is provided!", 
uno::Reference<uno::XInterface>(), 2);
+                }
 
                 m_nStartKeyGenerationID = nID;
             }
@@ -1722,8 +1724,12 @@ void SAL_CALL ZipPackage::setPropertyValue( const 
OUString& aPropertyName, const
             {
                 sal_Int32 nID = 0;
                 if ( !( rAlgorithm.Value >>= nID )
-                  || ( nID != xml::crypto::CipherID::AES_CBC_W3C_PADDING && 
nID != xml::crypto::CipherID::BLOWFISH_CFB_8 ) )
-                    throw IllegalArgumentException(THROW_WHERE "Unexpected 
start key generation algorithm is provided!", uno::Reference< uno::XInterface 
>(), 2 );
+                  || (nID != xml::crypto::CipherID::AES_GCM_W3C
+                      && nID != xml::crypto::CipherID::AES_CBC_W3C_PADDING
+                      && nID != xml::crypto::CipherID::BLOWFISH_CFB_8))
+                {
+                    throw IllegalArgumentException(THROW_WHERE "Unexpected 
encryption algorithm is provided!", uno::Reference<uno::XInterface>(), 2);
+                }
 
                 m_nCommonEncryptionID = nID;
             }
@@ -1732,7 +1738,9 @@ void SAL_CALL ZipPackage::setPropertyValue( const 
OUString& aPropertyName, const
                 sal_Int32 nID = 0;
                 if ( !( rAlgorithm.Value >>= nID )
                   || ( nID != xml::crypto::DigestID::SHA1_1K && nID != 
xml::crypto::DigestID::SHA256_1K ) )
-                    throw IllegalArgumentException(THROW_WHERE "Unexpected 
start key generation algorithm is provided!", uno::Reference< uno::XInterface 
>(), 2 );
+                {
+                    throw IllegalArgumentException(THROW_WHERE "Unexpected 
checksum algorithm is provided!", uno::Reference<uno::XInterface>(), 2);
+                }
 
                 m_nChecksumDigestID = nID;
             }
diff --git a/package/source/zippackage/ZipPackageStream.cxx 
b/package/source/zippackage/ZipPackageStream.cxx
index d3c80cec2dce..494d8aaa6efc 100644
--- a/package/source/zippackage/ZipPackageStream.cxx
+++ b/package/source/zippackage/ZipPackageStream.cxx
@@ -53,6 +53,7 @@
 
 #include <rtl/random.h>
 #include <sal/log.hxx>
+#include <o3tl/unreachable.hxx>
 #include <comphelper/diagnose_ex.hxx>
 
 #include <PackageConstants.hxx>
@@ -184,9 +185,19 @@ sal_Int32 ZipPackageStream::GetEncryptionAlgorithm() const
     return m_nImportedEncryptionAlgorithm ? m_nImportedEncryptionAlgorithm : 
m_rZipPackage.GetEncAlgID();
 }
 
-sal_Int32 ZipPackageStream::GetBlockSize() const
+sal_Int32 ZipPackageStream::GetIVSize() const
 {
-    return GetEncryptionAlgorithm() == 
css::xml::crypto::CipherID::AES_CBC_W3C_PADDING ? 16 : 8;
+    switch (GetEncryptionAlgorithm())
+    {
+        case css::xml::crypto::CipherID::BLOWFISH_CFB_8:
+            return 8;
+        case css::xml::crypto::CipherID::AES_CBC_W3C_PADDING:
+            return 16;
+        case css::xml::crypto::CipherID::AES_GCM_W3C:
+            return 12;
+        default:
+            O3TL_UNREACHABLE;
+    }
 }
 
 ::rtl::Reference<EncryptionData> ZipPackageStream::GetEncryptionData(Bugs 
const bugs)
@@ -567,7 +578,9 @@ bool ZipPackageStream::saveChild(
         {
             if ( bToBeEncrypted && !bTransportOwnEncrStreamAsRaw )
             {
-                uno::Sequence < sal_Int8 > aSalt( 16 ), aVector( 
GetBlockSize() );
+                uno::Sequence<sal_Int8> aSalt(16);
+                // note: for GCM it's particularly important that IV is unique
+                uno::Sequence<sal_Int8> aVector(GetIVSize());
                 rtl_random_getBytes ( rRandomPool, aSalt.getArray(), 16 );
                 rtl_random_getBytes ( rRandomPool, aVector.getArray(), 
aVector.getLength() );
                 if ( !m_bHaveOwnKey )
diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx
index 598b85aa07e8..8a303d1c331f 100644
--- a/sfx2/source/doc/objstor.cxx
+++ b/sfx2/source/doc/objstor.cxx
@@ -167,6 +167,11 @@ bool SfxObjectShell::QuerySlotExecutable( sal_uInt16 
/*nSlotId*/ )
     return true;
 }
 
+static bool UseODFWholesomeEncryption(SvtSaveOptions::ODFSaneDefaultVersion 
const nODFVersion)
+{
+    return nODFVersion == SvtSaveOptions::ODFSVER_LATEST_EXTENDED
+        && officecfg::Office::Common::Misc::ExperimentalMode::get();
+}
 
 bool GetEncryptionData_Impl( const SfxItemSet* pSet, uno::Sequence< 
beans::NamedValue >& o_rEncryptionData )
 {
@@ -358,8 +363,16 @@ void SfxObjectShell::SetupStorage( const uno::Reference< 
embed::XStorage >& xSto
 
         auto pEncryptionAlgs = aEncryptionAlgs.getArray();
         pEncryptionAlgs[0].Value <<= xml::crypto::DigestID::SHA256;
-        pEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K;
-        pEncryptionAlgs[1].Value <<= 
xml::crypto::CipherID::AES_CBC_W3C_PADDING;
+        if (UseODFWholesomeEncryption(nDefVersion))
+        {
+            pEncryptionAlgs[1].Value <<= xml::crypto::CipherID::AES_GCM_W3C;
+            pEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K;
+        }
+        else
+        {
+            pEncryptionAlgs[1].Value <<= 
xml::crypto::CipherID::AES_CBC_W3C_PADDING;
+            pEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K;
+        }
     }
 
     try
@@ -1225,8 +1238,7 @@ bool SfxObjectShell::SaveTo_Impl
     if (GetEncryptionData_Impl(&rMedium.GetItemSet(), aEncryptionData))
     {
         assert(aEncryptionData.getLength() != 0);
-        if (bOwnTarget && nVersion == SvtSaveOptions::ODFSVER_LATEST_EXTENDED
-            && officecfg::Office::Common::Misc::ExperimentalMode::get())
+        if (bOwnTarget && UseODFWholesomeEncryption(nVersion))
         {
             // when embedded objects are stored here, it should be called from
             // this function for the root document and encryption data was 
cleared
diff --git a/xmlsecurity/source/xmlsec/nss/ciphercontext.cxx 
b/xmlsecurity/source/xmlsec/nss/ciphercontext.cxx
index 220895a031f8..e0ef3bbe4ea7 100644
--- a/xmlsecurity/source/xmlsec/nss/ciphercontext.cxx
+++ b/xmlsecurity/source/xmlsec/nss/ciphercontext.cxx
@@ -20,7 +20,6 @@
 #include <sal/config.h>
 
 #include <com/sun/star/lang/DisposedException.hpp>
-#include <osl/diagnose.h>
 #include <rtl/random.h>
 #include <rtl/ref.hxx>
 #include <sal/log.hxx>
@@ -28,6 +27,9 @@
 #include "ciphercontext.hxx"
 #include <pk11pub.h>
 
+constexpr size_t nAESGCMIVSize = 12;
+constexpr size_t nAESGCMTagSize = 16;
+
 using namespace ::com::sun::star;
 
 uno::Reference< xml::crypto::XCipherContext > OCipherContext::Create( 
CK_MECHANISM_TYPE nNSSCipherID, const uno::Sequence< ::sal_Int8 >& aKey, const 
uno::Sequence< ::sal_Int8 >& aInitializationVector, bool bEncryption, bool 
bW3CPadding )
@@ -52,27 +54,50 @@ uno::Reference< xml::crypto::XCipherContext > 
OCipherContext::Create( CK_MECHANI
         throw uno::RuntimeException("PK11_ImportSymKey failed");
     }
 
-    SECItem aIVItem = { siBuffer,
-        const_cast<unsigned char*>(reinterpret_cast<const unsigned 
char*>(aInitializationVector.getConstArray())),
-        sal::static_int_cast<unsigned>(aInitializationVector.getLength()) };
-    xResult->m_pSecParam = PK11_ParamFromIV(nNSSCipherID, &aIVItem);
-    if (!xResult->m_pSecParam)
+    if (nNSSCipherID == CKM_AES_GCM)
     {
-        SAL_WARN("xmlsecurity.nss", "PK11_ParamFromIV failed");
-        throw uno::RuntimeException("PK11_ParamFromIV failed");
+        // TODO: when runtime requirements are raised to NSS 3.52, replace this
+        // according to https://fedoraproject.org/wiki/Changes/NssGCMParams
+        xResult->m_pSecParam = SECITEM_AllocItem(nullptr, nullptr, 
sizeof(CK_NSS_GCM_PARAMS));
+        if (!xResult->m_pSecParam)
+        {
+            SAL_WARN("xmlsecurity.nss", "SECITEM_AllocItem failed");
+            throw uno::RuntimeException("SECITEM_AllocItem failed");
+        }
+        assert(aInitializationVector.getLength() == nAESGCMIVSize);
+        xResult->m_AESGCMIV = aInitializationVector;
+        CK_NSS_GCM_PARAMS * pParams = 
reinterpret_cast<CK_NSS_GCM_PARAMS*>(xResult->m_pSecParam->data);
+        pParams->pIv = const_cast<unsigned char*>(reinterpret_cast<const 
unsigned char*>(xResult->m_AESGCMIV.getConstArray()));
+        pParams->ulIvLen = 
sal::static_int_cast<unsigned>(xResult->m_AESGCMIV.getLength());
+        pParams->pAAD = nullptr;
+        pParams->ulAADLen = 0;
+        pParams->ulTagBits = nAESGCMTagSize * 8;
     }
-
-    xResult->m_pContext = PK11_CreateContextBySymKey( nNSSCipherID, 
bEncryption ? CKA_ENCRYPT : CKA_DECRYPT, xResult->m_pSymKey, 
xResult->m_pSecParam);
-    if (!xResult->m_pContext)
+    else
     {
-        SAL_WARN("xmlsecurity.nss", "PK11_CreateContextBySymKey failed");
-        throw uno::RuntimeException("PK11_CreateContextBySymKey failed");
+        SECItem aIVItem = { siBuffer,
+            const_cast<unsigned char*>(reinterpret_cast<const unsigned 
char*>(aInitializationVector.getConstArray())),
+            sal::static_int_cast<unsigned>(aInitializationVector.getLength()) 
};
+        xResult->m_pSecParam = PK11_ParamFromIV(nNSSCipherID, &aIVItem);
+        if (!xResult->m_pSecParam)
+        {
+            SAL_WARN("xmlsecurity.nss", "PK11_ParamFromIV failed");
+            throw uno::RuntimeException("PK11_ParamFromIV failed");
+        }
+
+        xResult->m_pContext = PK11_CreateContextBySymKey( nNSSCipherID, 
bEncryption ? CKA_ENCRYPT : CKA_DECRYPT, xResult->m_pSymKey, 
xResult->m_pSecParam);
+        if (!xResult->m_pContext)
+        {
+            SAL_WARN("xmlsecurity.nss", "PK11_CreateContextBySymKey failed");
+            throw uno::RuntimeException("PK11_CreateContextBySymKey failed");
+        }
     }
 
     xResult->m_bEncryption = bEncryption;
     xResult->m_bW3CPadding = bW3CPadding;
     xResult->m_bPadding = bW3CPadding || ( PK11_GetPadMechanism( nNSSCipherID 
) == nNSSCipherID );
-    xResult->m_nBlockSize = PK11_GetBlockSize(nNSSCipherID, 
xResult->m_pSecParam);
+    // in NSS 3.94, a global default value of 8 is returned for CKM_AES_GCM
+    xResult->m_nBlockSize = nNSSCipherID == CKM_AES_GCM ? 16 : 
PK11_GetBlockSize(nNSSCipherID, xResult->m_pSecParam);
     if (SAL_MAX_INT8 < xResult->m_nBlockSize)
     {
         SAL_WARN("xmlsecurity.nss", "PK11_GetBlockSize unexpected result");
@@ -120,6 +145,18 @@ uno::Sequence< ::sal_Int8 > SAL_CALL 
OCipherContext::convertWithCipherContext( c
     if ( m_bDisposed )
         throw lang::DisposedException();
 
+    if (m_AESGCMIV.getLength())
+    {
+        if (SAL_MAX_INT32 - nAESGCMIVSize - nAESGCMTagSize <= 
static_cast<size_t>(m_aLastBlock.getLength()) + 
static_cast<size_t>(aData.getLength()))
+        {
+            m_bBroken = true;
+            throw uno::RuntimeException("overflow");
+        }
+        m_aLastBlock.realloc(m_aLastBlock.getLength() + aData.getLength());
+        memcpy(m_aLastBlock.getArray() + m_aLastBlock.getLength() - 
aData.getLength(), aData.getConstArray(), aData.getLength());
+        return {};
+    }
+
     uno::Sequence< sal_Int8 > aToConvert;
     if ( aData.hasElements() )
     {
@@ -201,6 +238,61 @@ uno::Sequence< ::sal_Int8 > SAL_CALL 
OCipherContext::finalizeCipherContextAndDis
     if ( m_bDisposed )
         throw lang::DisposedException();
 
+    if (m_AESGCMIV.getLength())
+    {
+        uno::Sequence<sal_Int8> aResult;
+        unsigned outLen;
+        if (m_bEncryption)
+        {
+            assert(sal::static_int_cast<size_t>(m_aLastBlock.getLength()) <= 
SAL_MAX_INT32 - nAESGCMIVSize - nAESGCMTagSize);
+            // add space for IV and tag
+            aResult.realloc(m_aLastBlock.getLength() + nAESGCMIVSize + 
nAESGCMTagSize);
+            // W3C xmlenc-core1 requires the IV preceding the ciphertext,
+            // but NSS doesn't do it, so copy it manually
+            memcpy(aResult.getArray(), m_AESGCMIV.getConstArray(), 
nAESGCMIVSize);
+            if (PK11_Encrypt(m_pSymKey, CKM_AES_GCM, m_pSecParam,
+                    reinterpret_cast<unsigned char*>(aResult.getArray() + 
nAESGCMIVSize),
+                    &outLen, aResult.getLength() - nAESGCMIVSize,
+                    reinterpret_cast<unsigned char 
const*>(m_aLastBlock.getConstArray()),
+                    m_aLastBlock.getLength()) != SECSuccess)
+            {
+                m_bBroken = true;
+                Dispose();
+                throw uno::RuntimeException("PK11_Encrypt failed");
+            }
+            assert(outLen == 
sal::static_int_cast<unsigned>(aResult.getLength() - nAESGCMIVSize));
+        }
+        else if (nAESGCMIVSize + nAESGCMTagSize < 
sal::static_int_cast<size_t>(m_aLastBlock.getLength()))
+        {
+            if (0 != memcmp(m_AESGCMIV.getConstArray(), 
m_aLastBlock.getConstArray(), nAESGCMIVSize))
+            {
+                m_bBroken = true;
+                Dispose();
+                throw uno::RuntimeException("inconsistent IV");
+            }
+            aResult.realloc(m_aLastBlock.getLength() - nAESGCMIVSize - 
nAESGCMTagSize);
+            if (PK11_Decrypt(m_pSymKey, CKM_AES_GCM, m_pSecParam,
+                    reinterpret_cast<unsigned char*>(aResult.getArray()),
+                    &outLen, aResult.getLength(),
+                    reinterpret_cast<unsigned char 
const*>(m_aLastBlock.getConstArray() + nAESGCMIVSize),
+                    m_aLastBlock.getLength() - nAESGCMIVSize) != SECSuccess)
+            {
+                m_bBroken = true;
+                Dispose();
+                throw uno::RuntimeException("PK11_Decrypt failed");
+            }
+            assert(outLen == 
sal::static_int_cast<unsigned>(aResult.getLength()));
+        }
+        else
+        {
+            m_bBroken = true;
+            Dispose();
+            throw uno::RuntimeException("incorrect size of input");
+        }
+        Dispose();
+        return aResult;
+    }
+
     assert(m_nBlockSize <= SAL_MAX_INT8);
     assert(m_nConverted % m_nBlockSize == 0); // whole blocks are converted
     sal_Int32 nSizeForPadding = ( m_nConverted + m_aLastBlock.getLength() ) % 
m_nBlockSize;
diff --git a/xmlsecurity/source/xmlsec/nss/ciphercontext.hxx 
b/xmlsecurity/source/xmlsec/nss/ciphercontext.hxx
index 40b610ef512e..d2c002ec4637 100644
--- a/xmlsecurity/source/xmlsec/nss/ciphercontext.hxx
+++ b/xmlsecurity/source/xmlsec/nss/ciphercontext.hxx
@@ -38,6 +38,7 @@ private:
 
     sal_Int32 m_nBlockSize;
     css::uno::Sequence< sal_Int8 > m_aLastBlock;
+    css::uno::Sequence<sal_Int8> m_AESGCMIV;
 
     bool m_bEncryption;
     bool m_bPadding;
diff --git a/xmlsecurity/source/xmlsec/nss/nssinitializer.cxx 
b/xmlsecurity/source/xmlsec/nss/nssinitializer.cxx
index 83a83a9ea551..bf74fa04ce64 100644
--- a/xmlsecurity/source/xmlsec/nss/nssinitializer.cxx
+++ b/xmlsecurity/source/xmlsec/nss/nssinitializer.cxx
@@ -587,11 +587,18 @@ css::uno::Reference< css::xml::crypto::XCipherContext > 
SAL_CALL ONSSInitializer
 {
     CK_MECHANISM_TYPE nNSSCipherID = 0;
     bool bW3CPadding = false;
-    if ( nCipherID != css::xml::crypto::CipherID::AES_CBC_W3C_PADDING )
-        throw css::lang::IllegalArgumentException("Unexpected cipher 
requested.", css::uno::Reference< css::uno::XInterface >(), 1 );
-
-    nNSSCipherID = CKM_AES_CBC;
-    bW3CPadding = true;
+    switch (nCipherID)
+    {
+        case css::xml::crypto::CipherID::AES_CBC_W3C_PADDING:
+            nNSSCipherID = CKM_AES_CBC;
+            bW3CPadding = true;
+            break;
+        case css::xml::crypto::CipherID::AES_GCM_W3C:
+            nNSSCipherID = CKM_AES_GCM;
+            break;
+        default:
+            throw css::lang::IllegalArgumentException("Unexpected cipher 
requested.", css::uno::Reference< css::uno::XInterface >(), 1);
+    }
 
     if ( aKey.getLength() != 16 && aKey.getLength() != 24 && aKey.getLength() 
!= 32 )
         throw css::lang::IllegalArgumentException("Unexpected key length.", 
css::uno::Reference< css::uno::XInterface >(), 2 );

Reply via email to