diff options
author | Michael Stahl <michael.stahl@allotropia.de> | 2023-12-19 19:13:00 +0100 |
---|---|---|
committer | Michael Stahl <michael.stahl@allotropia.de> | 2023-12-20 18:29:36 +0100 |
commit | 2f512aaa6c39390a5a0eb1d1e37f070127d068a4 (patch) | |
tree | 0e479309d04bf400ef7118168e37eff509f9da00 /package/source/zipapi | |
parent | 70ef230aae4f961c8197cc11a7ff5feaf1d96c20 (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.cxx | 2 | ||||
-rw-r--r-- | package/source/zipapi/ZipFile.cxx | 94 |
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; |