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 /xmlsecurity | |
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>
Diffstat (limited to 'xmlsecurity')
-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 |
3 files changed, 119 insertions, 19 deletions
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 ); |