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>
This commit is contained in:
Michael Stahl
2023-12-08 21:16:31 +01:00
parent 4bba7fbc22
commit f0fda7ad22
15 changed files with 226 additions and 33 deletions

View File

@@ -343,6 +343,7 @@ certain functionality.
@section package @section package
@li @c package @li @c package
@li @c package.manifest
@li @c package.xstor @li @c package.xstor
@li @c package.threadeddeflate @li @c package.threadeddeflate

View File

@@ -30,12 +30,25 @@ module com { module sun { module star { module xml { module crypto {
constants CipherID constants CipherID
{ {
/** identifier of AES algorithm in CBC mode with W3C padding /** 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; const long AES_CBC_W3C_PADDING = 1;
/** identifier of the Blowfish algorithm in 8-bit CFB mode /** identifier of the Blowfish algorithm in 8-bit CFB mode
*/ */
const long BLOWFISH_CFB_8 = 2; 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;
}; };

View File

@@ -31,6 +31,7 @@
#include <com/sun/star/xml/crypto/CipherID.hpp> #include <com/sun/star/xml/crypto/CipherID.hpp>
#include <comphelper/refcountedmutex.hxx> #include <comphelper/refcountedmutex.hxx>
#include <rtl/ref.hxx> #include <rtl/ref.hxx>
#include <o3tl/unreachable.hxx>
#include "HashMaps.hxx" #include "HashMaps.hxx"
#include "ZipFile.hxx" #include "ZipFile.hxx"
@@ -124,7 +125,18 @@ public:
sal_Int32 GetStartKeyGenID() const { return m_nStartKeyGenerationID; } sal_Int32 GetStartKeyGenID() const { return m_nStartKeyGenerationID; }
sal_Int32 GetEncAlgID() const { return m_nCommonEncryptionID; } sal_Int32 GetEncAlgID() const { return m_nCommonEncryptionID; }
sal_Int32 GetChecksumAlgID() const { return m_nChecksumDigestID; } 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; } rtl::Reference<comphelper::RefCountedMutex>& GetSharedMutexRef() { return m_aMutexHolder; }

View File

@@ -89,7 +89,7 @@ public:
sal_Int32 GetStartKeyGenID() const; sal_Int32 GetStartKeyGenID() const;
sal_Int32 GetEncryptionAlgorithm() const; sal_Int32 GetEncryptionAlgorithm() const;
sal_Int32 GetBlockSize() const; sal_Int32 GetIVSize() const;
void SetToBeCompressed (bool bNewValue) { m_bToBeCompressed = bNewValue;} void SetToBeCompressed (bool bNewValue) { m_bToBeCompressed = bNewValue;}
void SetIsEncrypted (bool bNewValue) { m_bIsEncrypted = bNewValue;} void SetIsEncrypted (bool bNewValue) { m_bIsEncrypted = bNewValue;}

View File

@@ -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 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 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 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 PBKDF2_NAME = u"PBKDF2"_ustr;
inline constexpr OUString PGP_NAME = u"PGP"_ustr; inline constexpr OUString PGP_NAME = u"PGP"_ustr;

View File

@@ -381,6 +381,15 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con
sEncAlgName = sAES256_URL; 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 ) else if ( nEncAlgID == xml::crypto::CipherID::BLOWFISH_CFB_8 )
{ {
sEncAlgName = sBlowfish_Name; sEncAlgName = sBlowfish_Name;

View File

@@ -21,6 +21,7 @@
#include "ManifestDefines.hxx" #include "ManifestDefines.hxx"
#include <PackageConstants.hxx> #include <PackageConstants.hxx>
#include <osl/diagnose.h> #include <osl/diagnose.h>
#include <sal/log.hxx>
#include <com/sun/star/xml/sax/XAttributeList.hpp> #include <com/sun/star/xml/sax/XAttributeList.hpp>
#include <com/sun/star/xml/crypto/DigestID.hpp> #include <com/sun/star/xml/crypto/DigestID.hpp>
#include <com/sun/star/xml/crypto/CipherID.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 ) { if ( aString == BLOWFISH_NAME || aString == BLOWFISH_URL ) {
aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty; aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
aSequence[PKG_MNFST_ENCALG].Value <<= xml::crypto::CipherID::BLOWFISH_CFB_8; 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 ) { } else if ( aString == AES256_URL ) {
aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty; aSequence[PKG_MNFST_ENCALG].Name = gsEncryptionAlgProperty;
aSequence[PKG_MNFST_ENCALG].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING; aSequence[PKG_MNFST_ENCALG].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING;

View File

@@ -93,6 +93,7 @@ XUnbufferedStream::XUnbufferedStream(
if ( bMustDecrypt ) if ( bMustDecrypt )
{ {
m_xCipherContext = ZipFile::StaticGetCipher( xContext, rData, false ); 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 ); mnBlockSize = ( rData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING ? 16 : 1 );
} }

View File

@@ -187,7 +187,8 @@ uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const un
throw ZipIOException("Can not create derived key!" ); 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; uno::Reference< uno::XComponentContext > xContext = xArgContext;
if ( !xContext.is() ) 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 ) 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() ) if ( !rData.is() || !rData->m_aKey.hasElements() )
return false; return false;

View File

@@ -1714,7 +1714,9 @@ void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const
sal_Int32 nID = 0; sal_Int32 nID = 0;
if ( !( rAlgorithm.Value >>= nID ) if ( !( rAlgorithm.Value >>= nID )
|| ( nID != xml::crypto::DigestID::SHA256 && nID != xml::crypto::DigestID::SHA1 ) ) || ( 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; m_nStartKeyGenerationID = nID;
} }
@@ -1722,8 +1724,12 @@ void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const
{ {
sal_Int32 nID = 0; sal_Int32 nID = 0;
if ( !( rAlgorithm.Value >>= nID ) if ( !( rAlgorithm.Value >>= nID )
|| ( nID != xml::crypto::CipherID::AES_CBC_W3C_PADDING && nID != xml::crypto::CipherID::BLOWFISH_CFB_8 ) ) || (nID != xml::crypto::CipherID::AES_GCM_W3C
throw IllegalArgumentException(THROW_WHERE "Unexpected start key generation algorithm is provided!", uno::Reference< uno::XInterface >(), 2 ); && 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; m_nCommonEncryptionID = nID;
} }
@@ -1732,7 +1738,9 @@ void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const
sal_Int32 nID = 0; sal_Int32 nID = 0;
if ( !( rAlgorithm.Value >>= nID ) if ( !( rAlgorithm.Value >>= nID )
|| ( nID != xml::crypto::DigestID::SHA1_1K && nID != xml::crypto::DigestID::SHA256_1K ) ) || ( 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; m_nChecksumDigestID = nID;
} }

View File

@@ -53,6 +53,7 @@
#include <rtl/random.h> #include <rtl/random.h>
#include <sal/log.hxx> #include <sal/log.hxx>
#include <o3tl/unreachable.hxx>
#include <comphelper/diagnose_ex.hxx> #include <comphelper/diagnose_ex.hxx>
#include <PackageConstants.hxx> #include <PackageConstants.hxx>
@@ -184,9 +185,19 @@ sal_Int32 ZipPackageStream::GetEncryptionAlgorithm() const
return m_nImportedEncryptionAlgorithm ? m_nImportedEncryptionAlgorithm : m_rZipPackage.GetEncAlgID(); 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) ::rtl::Reference<EncryptionData> ZipPackageStream::GetEncryptionData(Bugs const bugs)
@@ -567,7 +578,9 @@ bool ZipPackageStream::saveChild(
{ {
if ( bToBeEncrypted && !bTransportOwnEncrStreamAsRaw ) 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, aSalt.getArray(), 16 );
rtl_random_getBytes ( rRandomPool, aVector.getArray(), aVector.getLength() ); rtl_random_getBytes ( rRandomPool, aVector.getArray(), aVector.getLength() );
if ( !m_bHaveOwnKey ) if ( !m_bHaveOwnKey )

View File

@@ -167,6 +167,11 @@ bool SfxObjectShell::QuerySlotExecutable( sal_uInt16 /*nSlotId*/ )
return true; 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 ) 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(); auto pEncryptionAlgs = aEncryptionAlgs.getArray();
pEncryptionAlgs[0].Value <<= xml::crypto::DigestID::SHA256; pEncryptionAlgs[0].Value <<= xml::crypto::DigestID::SHA256;
if (UseODFWholesomeEncryption(nDefVersion))
{
pEncryptionAlgs[1].Value <<= xml::crypto::CipherID::AES_GCM_W3C;
pEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K; pEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K;
}
else
{
pEncryptionAlgs[1].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING; pEncryptionAlgs[1].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING;
pEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K;
}
} }
try try
@@ -1225,8 +1238,7 @@ bool SfxObjectShell::SaveTo_Impl
if (GetEncryptionData_Impl(&rMedium.GetItemSet(), aEncryptionData)) if (GetEncryptionData_Impl(&rMedium.GetItemSet(), aEncryptionData))
{ {
assert(aEncryptionData.getLength() != 0); assert(aEncryptionData.getLength() != 0);
if (bOwnTarget && nVersion == SvtSaveOptions::ODFSVER_LATEST_EXTENDED if (bOwnTarget && UseODFWholesomeEncryption(nVersion))
&& officecfg::Office::Common::Misc::ExperimentalMode::get())
{ {
// when embedded objects are stored here, it should be called from // when embedded objects are stored here, it should be called from
// this function for the root document and encryption data was cleared // this function for the root document and encryption data was cleared

View File

@@ -20,7 +20,6 @@
#include <sal/config.h> #include <sal/config.h>
#include <com/sun/star/lang/DisposedException.hpp> #include <com/sun/star/lang/DisposedException.hpp>
#include <osl/diagnose.h>
#include <rtl/random.h> #include <rtl/random.h>
#include <rtl/ref.hxx> #include <rtl/ref.hxx>
#include <sal/log.hxx> #include <sal/log.hxx>
@@ -28,6 +27,9 @@
#include "ciphercontext.hxx" #include "ciphercontext.hxx"
#include <pk11pub.h> #include <pk11pub.h>
constexpr size_t nAESGCMIVSize = 12;
constexpr size_t nAESGCMTagSize = 16;
using namespace ::com::sun::star; 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 ) 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,6 +54,27 @@ uno::Reference< xml::crypto::XCipherContext > OCipherContext::Create( CK_MECHANI
throw uno::RuntimeException("PK11_ImportSymKey failed"); throw uno::RuntimeException("PK11_ImportSymKey failed");
} }
if (nNSSCipherID == CKM_AES_GCM)
{
// 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;
}
else
{
SECItem aIVItem = { siBuffer, SECItem aIVItem = { siBuffer,
const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(aInitializationVector.getConstArray())), const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(aInitializationVector.getConstArray())),
sal::static_int_cast<unsigned>(aInitializationVector.getLength()) }; sal::static_int_cast<unsigned>(aInitializationVector.getLength()) };
@@ -68,11 +91,13 @@ uno::Reference< xml::crypto::XCipherContext > OCipherContext::Create( CK_MECHANI
SAL_WARN("xmlsecurity.nss", "PK11_CreateContextBySymKey failed"); SAL_WARN("xmlsecurity.nss", "PK11_CreateContextBySymKey failed");
throw uno::RuntimeException("PK11_CreateContextBySymKey failed"); throw uno::RuntimeException("PK11_CreateContextBySymKey failed");
} }
}
xResult->m_bEncryption = bEncryption; xResult->m_bEncryption = bEncryption;
xResult->m_bW3CPadding = bW3CPadding; xResult->m_bW3CPadding = bW3CPadding;
xResult->m_bPadding = bW3CPadding || ( PK11_GetPadMechanism( nNSSCipherID ) == nNSSCipherID ); 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) if (SAL_MAX_INT8 < xResult->m_nBlockSize)
{ {
SAL_WARN("xmlsecurity.nss", "PK11_GetBlockSize unexpected result"); SAL_WARN("xmlsecurity.nss", "PK11_GetBlockSize unexpected result");
@@ -120,6 +145,18 @@ uno::Sequence< ::sal_Int8 > SAL_CALL OCipherContext::convertWithCipherContext( c
if ( m_bDisposed ) if ( m_bDisposed )
throw lang::DisposedException(); 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; uno::Sequence< sal_Int8 > aToConvert;
if ( aData.hasElements() ) if ( aData.hasElements() )
{ {
@@ -201,6 +238,61 @@ uno::Sequence< ::sal_Int8 > SAL_CALL OCipherContext::finalizeCipherContextAndDis
if ( m_bDisposed ) if ( m_bDisposed )
throw lang::DisposedException(); 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_nBlockSize <= SAL_MAX_INT8);
assert(m_nConverted % m_nBlockSize == 0); // whole blocks are converted assert(m_nConverted % m_nBlockSize == 0); // whole blocks are converted
sal_Int32 nSizeForPadding = ( m_nConverted + m_aLastBlock.getLength() ) % m_nBlockSize; sal_Int32 nSizeForPadding = ( m_nConverted + m_aLastBlock.getLength() ) % m_nBlockSize;

View File

@@ -38,6 +38,7 @@ private:
sal_Int32 m_nBlockSize; sal_Int32 m_nBlockSize;
css::uno::Sequence< sal_Int8 > m_aLastBlock; css::uno::Sequence< sal_Int8 > m_aLastBlock;
css::uno::Sequence<sal_Int8> m_AESGCMIV;
bool m_bEncryption; bool m_bEncryption;
bool m_bPadding; bool m_bPadding;

View File

@@ -587,11 +587,18 @@ css::uno::Reference< css::xml::crypto::XCipherContext > SAL_CALL ONSSInitializer
{ {
CK_MECHANISM_TYPE nNSSCipherID = 0; CK_MECHANISM_TYPE nNSSCipherID = 0;
bool bW3CPadding = false; bool bW3CPadding = false;
if ( nCipherID != css::xml::crypto::CipherID::AES_CBC_W3C_PADDING ) switch (nCipherID)
throw css::lang::IllegalArgumentException("Unexpected cipher requested.", css::uno::Reference< css::uno::XInterface >(), 1 ); {
case css::xml::crypto::CipherID::AES_CBC_W3C_PADDING:
nNSSCipherID = CKM_AES_CBC; nNSSCipherID = CKM_AES_CBC;
bW3CPadding = true; 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 ) if ( aKey.getLength() != 16 && aKey.getLength() != 24 && aKey.getLength() != 32 )
throw css::lang::IllegalArgumentException("Unexpected key length.", css::uno::Reference< css::uno::XInterface >(), 2 ); throw css::lang::IllegalArgumentException("Unexpected key length.", css::uno::Reference< css::uno::XInterface >(), 2 );