summaryrefslogtreecommitdiff
path: root/xmlsecurity
diff options
context:
space:
mode:
authorMichael Stahl <michael.stahl@allotropia.de>2023-12-08 21:16:31 +0100
committerMichael Stahl <michael.stahl@allotropia.de>2023-12-11 20:46:40 +0100
commitf0fda7ad2236f478fea396a23d4f982e5fc37e68 (patch)
treee6b0afbeb4874453aeb572c6e21240220667a36d /xmlsecurity
parent4bba7fbc22f13d579e57b36e8c8e302d987e01f0 (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.cxx120
-rw-r--r--xmlsecurity/source/xmlsec/nss/ciphercontext.hxx1
-rw-r--r--xmlsecurity/source/xmlsec/nss/nssinitializer.cxx17
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 );