summaryrefslogtreecommitdiff
path: root/package/source/zipapi
diff options
context:
space:
mode:
authorMichael Stahl <michael.stahl@allotropia.de>2023-12-19 19:13:00 +0100
committerMichael Stahl <michael.stahl@allotropia.de>2023-12-20 18:29:36 +0100
commit2f512aaa6c39390a5a0eb1d1e37f070127d068a4 (patch)
tree0e479309d04bf400ef7118168e37eff509f9da00 /package/source/zipapi
parent70ef230aae4f961c8197cc11a7ff5feaf1d96c20 (diff)
tdf#105844 offapi,package,sfx2: use Argon2 for wholesome ODF encryption
https://www.rfc-editor.org/rfc/rfc9106.html * add css::xml::crypto::KDFID constant group * add "KeyDerivationFunction" to setEncryptionAlgorithms sequence * Argon2 is used by default for wholesome ODF encryption, but $LO_ARGON2_DISABLE can be set to use PBKDF2 * extend various structs in package * use 3 new ODF attributes "loext:argon2-iterations" "loext:argon2-memory" "loext:argon2-lanes" to store the arguments * use this URL for now: "urn:org:documentfoundation:names:experimental:office:manifest:argon2id" * use default arguments according to second recommendation from "7.4. Recommendations" of RFC9106; 64 MiB RAM should hopefully not be too much even for 32 bit builds Change-Id: I683118cc5e0706bd6544db6fb909096768ac9920 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161009 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
Diffstat (limited to 'package/source/zipapi')
-rw-r--r--package/source/zipapi/XUnbufferedStream.cxx2
-rw-r--r--package/source/zipapi/ZipFile.cxx94
2 files changed, 90 insertions, 6 deletions
diff --git a/package/source/zipapi/XUnbufferedStream.cxx b/package/source/zipapi/XUnbufferedStream.cxx
index 350f142c25ba..8b628b14ddfe 100644
--- a/package/source/zipapi/XUnbufferedStream.cxx
+++ b/package/source/zipapi/XUnbufferedStream.cxx
@@ -85,7 +85,7 @@ XUnbufferedStream::XUnbufferedStream(
throw ZipIOException("Integer-overflow");
bool bHaveEncryptData = rData.is() && rData->m_aInitVector.hasElements() &&
- ((rData->m_aSalt.hasElements() && rData->m_nIterationCount != 0)
+ ((rData->m_aSalt.hasElements() && (rData->m_oPBKDFIterationCount || rData->m_oArgon2Args))
||
rData->m_aKey.hasElements());
bool bMustDecrypt = nStreamMode == UNBUFF_STREAM_DATA && bHaveEncryptData && bIsEncrypted;
diff --git a/package/source/zipapi/ZipFile.cxx b/package/source/zipapi/ZipFile.cxx
index bdcd8610be60..6137e3a0bb0a 100644
--- a/package/source/zipapi/ZipFile.cxx
+++ b/package/source/zipapi/ZipFile.cxx
@@ -34,6 +34,7 @@
#include <comphelper/bytereader.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/processfactory.hxx>
+#include <comphelper/threadpool.hxx>
#include <rtl/digest.h>
#include <sal/log.hxx>
#include <o3tl/safeint.hxx>
@@ -44,6 +45,8 @@
#include <utility>
#include <vector>
+#include <argon2.h>
+
#include "blowfishcontext.hxx"
#include "sha1context.hxx"
#include <ZipFile.hxx>
@@ -173,20 +176,52 @@ uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const un
}
uno::Sequence< sal_Int8 > aDerivedKey( xEncryptionData->m_nDerivedKeySize );
- if ( !xEncryptionData->m_nIterationCount &&
- xEncryptionData->m_nDerivedKeySize == xEncryptionData->m_aKey.getLength() )
+ if (!xEncryptionData->m_oPBKDFIterationCount && !xEncryptionData->m_oArgon2Args
+ && xEncryptionData->m_nDerivedKeySize == xEncryptionData->m_aKey.getLength())
{
// gpg4libre: no need to derive key, m_aKey is already
// usable as symmetric session key
aDerivedKey = xEncryptionData->m_aKey;
}
+ else if (xEncryptionData->m_oArgon2Args)
+ {
+ // apparently multiple lanes cannot be processed in parallel (the
+ // implementation will clamp), but it doesn't make sense to have more
+ // threads than CPUs
+ uint32_t const threads(::comphelper::ThreadPool::getPreferredConcurrency());
+ // need to use context to set a fixed version
+ argon2_context context = {
+ .out = reinterpret_cast<uint8_t *>(aDerivedKey.getArray()),
+ .outlen = ::sal::static_int_cast<uint32_t>(aDerivedKey.getLength()),
+ .pwd = reinterpret_cast<uint8_t *>(xEncryptionData->m_aKey.getArray()),
+ .pwdlen = ::sal::static_int_cast<uint32_t>(xEncryptionData->m_aKey.getLength()),
+ .salt = reinterpret_cast<uint8_t *>(xEncryptionData->m_aSalt.getArray()),
+ .saltlen = ::sal::static_int_cast<uint32_t>(xEncryptionData->m_aSalt.getLength()),
+ .secret = nullptr, .secretlen = 0,
+ .ad = nullptr, .adlen = 0,
+ .t_cost = ::sal::static_int_cast<uint32_t>(::std::get<0>(*xEncryptionData->m_oArgon2Args)),
+ .m_cost = ::sal::static_int_cast<uint32_t>(::std::get<1>(*xEncryptionData->m_oArgon2Args)),
+ .lanes = ::sal::static_int_cast<uint32_t>(::std::get<2>(*xEncryptionData->m_oArgon2Args)),
+ .threads = threads,
+ .version = ARGON2_VERSION_13,
+ .allocate_cbk = nullptr, .free_cbk = nullptr,
+ .flags = ARGON2_DEFAULT_FLAGS
+ };
+ // libargon2 validates all the arguments so don't need to do it here
+ int const rc = argon2id_ctx(&context);
+ if (rc != ARGON2_OK)
+ {
+ SAL_WARN("package", "argon2id_ctx failed to derive key: " << argon2_error_message(rc));
+ throw ZipIOException("argon2id_ctx failed to derive key");
+ }
+ }
else if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ),
aDerivedKey.getLength(),
reinterpret_cast< const sal_uInt8 * > (xEncryptionData->m_aKey.getConstArray() ),
xEncryptionData->m_aKey.getLength(),
reinterpret_cast< const sal_uInt8 * > ( xEncryptionData->m_aSalt.getConstArray() ),
xEncryptionData->m_aSalt.getLength(),
- xEncryptionData->m_nIterationCount ) )
+ *xEncryptionData->m_oPBKDFIterationCount) )
{
throw ZipIOException("Can not create derived key!" );
}
@@ -236,12 +271,30 @@ void ZipFile::StaticFillHeader( const ::rtl::Reference< EncryptionData >& rData,
*(pHeader++) = ( n_ConstCurrentVersion >> 8 ) & 0xFF;
// Then the iteration Count
- sal_Int32 nIterationCount = rData->m_nIterationCount;
+ sal_Int32 const nIterationCount = rData->m_oPBKDFIterationCount ? *rData->m_oPBKDFIterationCount : 0;
*(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 0 ) & 0xFF);
*(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 8 ) & 0xFF);
*(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 16 ) & 0xFF);
*(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 24 ) & 0xFF);
+ sal_Int32 const nArgon2t = rData->m_oArgon2Args ? ::std::get<0>(*rData->m_oArgon2Args) : 0;
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 0) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 8) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 16) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2t >> 24) & 0xFF);
+
+ sal_Int32 const nArgon2m = rData->m_oArgon2Args ? ::std::get<1>(*rData->m_oArgon2Args) : 0;
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 0) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 8) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 16) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2m >> 24) & 0xFF);
+
+ sal_Int32 const nArgon2p = rData->m_oArgon2Args ? ::std::get<2>(*rData->m_oArgon2Args) : 0;
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 0) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 8) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 16) & 0xFF);
+ *(pHeader++) = static_cast<sal_Int8>((nArgon2p >> 24) & 0xFF);
+
// FIXME64: need to handle larger sizes
// Then the size:
*(pHeader++) = static_cast< sal_Int8 >(( nSize >> 0 ) & 0xFF);
@@ -334,7 +387,38 @@ bool ZipFile::StaticFillData ( ::rtl::Reference< BaseEncryptionData > const & r
nCount |= ( pBuffer[nPos++] & 0xFF ) << 8;
nCount |= ( pBuffer[nPos++] & 0xFF ) << 16;
nCount |= ( pBuffer[nPos++] & 0xFF ) << 24;
- rData->m_nIterationCount = nCount;
+ if (nCount != 0)
+ {
+ rData->m_oPBKDFIterationCount.emplace(nCount);
+ }
+ else
+ {
+ rData->m_oPBKDFIterationCount.reset();
+ }
+
+ sal_Int32 nArgon2t = pBuffer[nPos++] & 0xFF;
+ nArgon2t |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ nArgon2t |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ nArgon2t |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ sal_Int32 nArgon2m = pBuffer[nPos++] & 0xFF;
+ nArgon2m |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ nArgon2m |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ nArgon2m |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ sal_Int32 nArgon2p = pBuffer[nPos++] & 0xFF;
+ nArgon2p |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ nArgon2p |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ nArgon2p |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ if (nArgon2t != 0 && nArgon2m != 0 && nArgon2p != 0)
+ {
+ rData->m_oArgon2Args.emplace(nArgon2t, nArgon2m, nArgon2p);
+ }
+ else
+ {
+ rData->m_oArgon2Args.reset();
+ }
rSize = pBuffer[nPos++] & 0xFF;
rSize |= ( pBuffer[nPos++] & 0xFF ) << 8;