diff options
author | Michael Stahl <michael.stahl@allotropia.de> | 2023-12-08 21:16:31 +0100 |
---|---|---|
committer | Michael Stahl <michael.stahl@allotropia.de> | 2023-12-11 20:46:40 +0100 |
commit | f0fda7ad2236f478fea396a23d4f982e5fc37e68 (patch) | |
tree | e6b0afbeb4874453aeb572c6e21240220667a36d | |
parent | 4bba7fbc22f13d579e57b36e8c8e302d987e01f0 (diff) |
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.stahl@allotropia.de>
-rw-r--r-- | include/sal/log-areas.dox | 1 | ||||
-rw-r--r-- | offapi/com/sun/star/xml/crypto/CipherID.idl | 13 | ||||
-rw-r--r-- | package/inc/ZipPackage.hxx | 14 | ||||
-rw-r--r-- | package/inc/ZipPackageStream.hxx | 2 | ||||
-rw-r--r-- | package/source/manifest/ManifestDefines.hxx | 3 | ||||
-rw-r--r-- | package/source/manifest/ManifestExport.cxx | 9 | ||||
-rw-r--r-- | package/source/manifest/ManifestImport.cxx | 17 | ||||
-rw-r--r-- | package/source/zipapi/XUnbufferedStream.cxx | 1 | ||||
-rw-r--r-- | package/source/zipapi/ZipFile.cxx | 6 | ||||
-rw-r--r-- | package/source/zippackage/ZipPackage.cxx | 16 | ||||
-rw-r--r-- | package/source/zippackage/ZipPackageStream.cxx | 19 | ||||
-rw-r--r-- | sfx2/source/doc/objstor.cxx | 20 | ||||
-rw-r--r-- | xmlsecurity/source/xmlsec/nss/ciphercontext.cxx | 120 | ||||
-rw-r--r-- | xmlsecurity/source/xmlsec/nss/ciphercontext.hxx | 1 | ||||
-rw-r--r-- | xmlsecurity/source/xmlsec/nss/nssinitializer.cxx | 17 |
15 files changed, 226 insertions, 33 deletions
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 ); |