summaryrefslogtreecommitdiff
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
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>
-rwxr-xr-xbin/lo-all-static-libs1
-rw-r--r--external/argon2/0001-Fix-possible-compiler-error-due-to-undefined-_MSC_VE.patch32
-rw-r--r--external/argon2/UnpackedTarball_argon2.mk6
-rw-r--r--offapi/UnoApi_offapi.mk1
-rw-r--r--offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl8
-rw-r--r--offapi/com/sun/star/xml/crypto/KDFID.idl47
-rw-r--r--package/Library_package2.mk1
-rw-r--r--package/inc/EncryptedDataHeader.hxx9
-rw-r--r--package/inc/EncryptionData.hxx10
-rw-r--r--package/inc/PackageConstants.hxx5
-rw-r--r--package/inc/ZipPackage.hxx1
-rw-r--r--package/inc/ZipPackageEntry.hxx5
-rw-r--r--package/inc/ZipPackageFolder.hxx6
-rw-r--r--package/inc/ZipPackageStream.hxx14
-rw-r--r--package/source/manifest/ManifestDefines.hxx5
-rw-r--r--package/source/manifest/ManifestExport.cxx43
-rw-r--r--package/source/manifest/ManifestImport.cxx47
-rw-r--r--package/source/zipapi/XUnbufferedStream.cxx2
-rw-r--r--package/source/zipapi/ZipFile.cxx94
-rw-r--r--package/source/zippackage/ZipPackage.cxx77
-rw-r--r--package/source/zippackage/ZipPackageFolder.cxx20
-rw-r--r--package/source/zippackage/ZipPackageStream.cxx56
-rw-r--r--sfx2/source/doc/objstor.cxx8
23 files changed, 428 insertions, 70 deletions
diff --git a/bin/lo-all-static-libs b/bin/lo-all-static-libs
index 009ddce23730..dd85567f188d 100755
--- a/bin/lo-all-static-libs
+++ b/bin/lo-all-static-libs
@@ -110,6 +110,7 @@ echo $INSTDIR/$LIBO_LIB_FOLDER/lib*.a \
$foolibs \
$WORKDIR/LinkTarget/StaticLibrary/lib*.a \
$oslibs \
+ $WORKDIR/UnpackedTarball/argon2/libargon2.a \
$WORKDIR/UnpackedTarball/icu/source/lib/*.a \
$WORKDIR/UnpackedTarball/liblangtag/liblangtag/.libs/*.a \
$WORKDIR/UnpackedTarball/lcms2/src/.libs/*.a \
diff --git a/external/argon2/0001-Fix-possible-compiler-error-due-to-undefined-_MSC_VE.patch b/external/argon2/0001-Fix-possible-compiler-error-due-to-undefined-_MSC_VE.patch
new file mode 100644
index 000000000000..538b41e3ec9f
--- /dev/null
+++ b/external/argon2/0001-Fix-possible-compiler-error-due-to-undefined-_MSC_VE.patch
@@ -0,0 +1,32 @@
+From 48829f87ebafbb9938d23a8f0bff4d11d770690e Mon Sep 17 00:00:00 2001
+From: Patrick Steinhardt <ps@pks.im>
+Date: Thu, 20 Feb 2020 17:37:32 +0100
+Subject: [PATCH] Fix possible compiler error due to undefined _MSC_VER
+
+In order to determine how to set up the ARGON2_PUBLIC and ARGON2_LOCAL
+macros, we check for various different environments via preprocessor
+defines. For Microsoft Visual Studio, we check that the macro _MSC_VER
+evaluates to non-zero via `#elif _MSC_VER`. This may raise a compile
+error when compiling with "-Werror=undef" if the variable isn't defined.
+
+Fix the issue by using `#elif defined(_MSC_VER)` instead.
+---
+ include/argon2.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/argon2.h b/include/argon2.h
+index fc8682c..1b471f6 100644
+--- a/include/argon2.h
++++ b/include/argon2.h
+@@ -30,7 +30,7 @@ extern "C" {
+ #ifdef A2_VISCTL
+ #define ARGON2_PUBLIC __attribute__((visibility("default")))
+ #define ARGON2_LOCAL __attribute__ ((visibility ("hidden")))
+-#elif _MSC_VER
++#elif defined(_MSC_VER)
+ #define ARGON2_PUBLIC __declspec(dllexport)
+ #define ARGON2_LOCAL
+ #else
+--
+2.43.0
+
diff --git a/external/argon2/UnpackedTarball_argon2.mk b/external/argon2/UnpackedTarball_argon2.mk
index 1d314cfb08d9..0b35c2f58caf 100644
--- a/external/argon2/UnpackedTarball_argon2.mk
+++ b/external/argon2/UnpackedTarball_argon2.mk
@@ -11,4 +11,10 @@ $(eval $(call gb_UnpackedTarball_UnpackedTarball,argon2))
$(eval $(call gb_UnpackedTarball_set_tarball,argon2,$(ARGON2_TARBALL)))
+$(eval $(call gb_UnpackedTarball_set_patchlevel,argon2,1))
+
+$(eval $(call gb_UnpackedTarball_add_patches,argon2,\
+ external/argon2/0001-Fix-possible-compiler-error-due-to-undefined-_MSC_VE.patch \
+))
+
# vim: set noet sw=4 ts=4:
diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index 2e2ee40aa8b4..186c68d500fb 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -4276,6 +4276,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/xml,\
$(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/xml/crypto,\
CipherID \
DigestID \
+ KDFID \
SecurityOperationStatus \
XCertificateCreator \
XCipherContext \
diff --git a/offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl b/offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl
index 71c5695f482f..da14714c6223 100644
--- a/offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl
+++ b/offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl
@@ -48,6 +48,14 @@ interface XEncryptionProtectedStorage: XEncryptionProtectedSource2
error; it should take values from
com::sun::star::xml:crypto::DigestID.
</dd>
+ <dt>KeyDerivationFunction</dt>
+ <dd>
+ specifies the algorithm that was used to derive the
+ encryption key from the password; it is applied to
+ the result of the StartKeyGenerationAlgorithm;
+ it should take values from
+ com::sun::star::xml:crypto::KDFID.
+ </dd>
<dt>EncryptionAlgorithm</dt>
<dd>
specifies the algorithm that should be used to
diff --git a/offapi/com/sun/star/xml/crypto/KDFID.idl b/offapi/com/sun/star/xml/crypto/KDFID.idl
new file mode 100644
index 000000000000..dc58e6b7463e
--- /dev/null
+++ b/offapi/com/sun/star/xml/crypto/KDFID.idl
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+module com { module sun { module star { module xml { module crypto {
+
+/** Constants to identify Key Derivation Function
+ @since LibreOffice 24.2
+ */
+constants KDFID
+{
+ /** PBKDF2
+
+ Derive key material from password. When used with ODF, the
+ "StartKeyGenerationAlgorithm" is applied to the password and the
+ result is passed to KDF.
+ */
+ const long PBKDF2 = 1;
+
+ /** OpenPGP/GnuPG
+
+ Of course this is public key encryption, but it does produce
+ key material for symmetric encryption. When used with ODF, the
+ "StartKeyGenerationAlgorithm" digest is not used, as the input
+ is not a password.
+ */
+ const long PGP_RSA_OAEP_MGF1P = 2;
+
+ /** Argon2id
+
+ Derive key material from password. When used with ODF, the
+ "StartKeyGenerationAlgorithm" is applied to the password and the
+ result is passed to KDF.
+
+ @see https://www.rfc-editor.org/rfc/rfc9106.html
+ */
+ const long Argon2id = 3;
+};
+
+}; }; }; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/package/Library_package2.mk b/package/Library_package2.mk
index 6f29bf002a6b..2ddbc31caf85 100644
--- a/package/Library_package2.mk
+++ b/package/Library_package2.mk
@@ -42,6 +42,7 @@ $(eval $(call gb_Library_use_libraries,package2,\
$(eval $(call gb_Library_use_externals,package2,\
boost_headers \
+ argon2 \
zlib \
))
diff --git a/package/inc/EncryptedDataHeader.hxx b/package/inc/EncryptedDataHeader.hxx
index 6860c50abb48..2f92dea60a28 100644
--- a/package/inc/EncryptedDataHeader.hxx
+++ b/package/inc/EncryptedDataHeader.hxx
@@ -25,7 +25,10 @@
Header signature 4 bytes
Version number 2 bytes
- Iteration count 4 bytes
+ PBKDF2 Iteration count 4 bytes
+ Argon2 t_cost 4 bytes
+ Argon2 m_cost 4 bytes
+ Argon2 lanes 4 bytes
Size 4 bytes
EncAlgorithm 4 bytes
DigestAlgorithm 4 bytes
@@ -43,8 +46,8 @@
*/
const sal_uInt32 n_ConstHeader = 0x05024d4dL; // "MM\002\005"
const sal_Int32 n_ConstHeaderSize
- = 38; // + salt length + iv length + digest length + mediatype length
-const sal_Int16 n_ConstCurrentVersion = 1;
+ = 50; // + salt length + iv length + digest length + mediatype length
+const sal_Int16 n_ConstCurrentVersion = 2;
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/inc/EncryptionData.hxx b/package/inc/EncryptionData.hxx
index f2d5c0b126b0..b505b9c2277a 100644
--- a/package/inc/EncryptionData.hxx
+++ b/package/inc/EncryptionData.hxx
@@ -23,6 +23,7 @@
#include <cppuhelper/weak.hxx>
#include <optional>
+#include <tuple>
class BaseEncryptionData : public cppu::OWeakObject
{
@@ -30,17 +31,20 @@ public:
css::uno::Sequence< sal_Int8 > m_aSalt;
css::uno::Sequence< sal_Int8 > m_aInitVector;
css::uno::Sequence< sal_Int8 > m_aDigest;
- sal_Int32 m_nIterationCount;
+ ::std::optional<sal_Int32> m_oPBKDFIterationCount;
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> m_oArgon2Args;
BaseEncryptionData()
- : m_nIterationCount ( 0 ){}
+ {
+ }
BaseEncryptionData( const BaseEncryptionData& aData )
: cppu::OWeakObject()
, m_aSalt( aData.m_aSalt )
, m_aInitVector( aData.m_aInitVector )
, m_aDigest( aData.m_aDigest )
- , m_nIterationCount( aData.m_nIterationCount )
+ , m_oPBKDFIterationCount(aData.m_oPBKDFIterationCount)
+ , m_oArgon2Args(aData.m_oArgon2Args)
{}
};
diff --git a/package/inc/PackageConstants.hxx b/package/inc/PackageConstants.hxx
index 31c0928e3c30..ce89204b4348 100644
--- a/package/inc/PackageConstants.hxx
+++ b/package/inc/PackageConstants.hxx
@@ -31,6 +31,7 @@ const sal_Int32 n_ConstDigestLength = 1024;
const sal_Int32 n_ConstDigestDecrypt = 1056; // 1024 + 32
// the constants related to the manifest.xml entries
+// these primarily exist so that ManifestImport can directly write into Sequence
#define PKG_MNFST_FULLPATH 0 //FullPath (Put full-path property first for MBA)
#define PKG_MNFST_VERSION 1 //Version
#define PKG_MNFST_MEDIATYPE 2 //MediaType
@@ -44,9 +45,11 @@ const sal_Int32 n_ConstDigestDecrypt = 1056; // 1024 + 32
#define PKG_MNFST_STARTALG 9 //StartKeyAlgorithm
#define PKG_MNFST_DIGESTALG 10 //DigestAlgorithm
#define PKG_MNFST_DERKEYSIZE 11 //DerivedKeySize
+#define PKG_MNFST_KDF 12 // KeyDerivationFunction
+#define PKG_MNFST_ARGON2ARGS 13 // Argon2 arguments
#define PKG_SIZE_NOENCR_MNFST 3
-#define PKG_SIZE_ENCR_MNFST 12
+#define PKG_SIZE_ENCR_MNFST 14 // max size
// the properties related constants
inline constexpr OUString ENCRYPTION_KEY_PROPERTY = u"EncryptionKey"_ustr;
diff --git a/package/inc/ZipPackage.hxx b/package/inc/ZipPackage.hxx
index 5d196fe7cdaa..dbfbfe1bc17d 100644
--- a/package/inc/ZipPackage.hxx
+++ b/package/inc/ZipPackage.hxx
@@ -79,6 +79,7 @@ class ZipPackage final : public cppu::WeakImplHelper
sal_Int32 m_nStartKeyGenerationID;
::std::optional<sal_Int32> m_oChecksumDigestID;
+ sal_Int32 m_nKeyDerivationFunctionID;
sal_Int32 m_nCommonEncryptionID;
bool m_bHasEncryptedEntries;
bool m_bHasNonEncryptedEntries;
diff --git a/package/inc/ZipPackageEntry.hxx b/package/inc/ZipPackageEntry.hxx
index 27ad017aa859..f25cdc19bdc9 100644
--- a/package/inc/ZipPackageEntry.hxx
+++ b/package/inc/ZipPackageEntry.hxx
@@ -29,6 +29,8 @@
#include <cppuhelper/implbase.hxx>
#include <vector>
+#include <optional>
+#include <tuple>
typedef void* rtlRandomPool;
class ZipOutputStream;
@@ -66,7 +68,8 @@ public:
std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const css::uno::Sequence < sal_Int8 >& rEncryptionKey,
- sal_Int32 nPBKDF2IterationCount,
+ ::std::optional<sal_Int32> oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args,
const rtlRandomPool &rRandomPool ) = 0;
void clearParent()
diff --git a/package/inc/ZipPackageFolder.hxx b/package/inc/ZipPackageFolder.hxx
index edc46e9c386b..2b1b98191302 100644
--- a/package/inc/ZipPackageFolder.hxx
+++ b/package/inc/ZipPackageFolder.hxx
@@ -98,7 +98,8 @@ public:
std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const css::uno::Sequence < sal_Int8 >& rEncryptionKey,
- sal_Int32 nPBKDF2IterationCount,
+ ::std::optional<sal_Int32> oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args,
const rtlRandomPool &rRandomPool ) override;
// Recursive functions
@@ -108,7 +109,8 @@ public:
std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const css::uno::Sequence< sal_Int8 > &rEncryptionKey,
- sal_Int32 nPBKDF2IterationCount,
+ ::std::optional<sal_Int32> oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args,
const rtlRandomPool & rRandomPool) const;
// XNameContainer
diff --git a/package/inc/ZipPackageStream.hxx b/package/inc/ZipPackageStream.hxx
index b39c59e633e9..0cb52e88c892 100644
--- a/package/inc/ZipPackageStream.hxx
+++ b/package/inc/ZipPackageStream.hxx
@@ -29,7 +29,6 @@
#include "EncryptionData.hxx"
-#include <optional>
#define PACKAGE_STREAM_NOTSET 0
#define PACKAGE_STREAM_PACKAGEMEMBER 1
@@ -115,8 +114,14 @@ public:
{ m_xBaseEncryptionData->m_aSalt = rNewSalt;}
void setDigest (const css::uno::Sequence < sal_Int8 >& rNewDigest )
{ m_xBaseEncryptionData->m_aDigest = rNewDigest;}
- void setIterationCount (const sal_Int32 nNewCount)
- { m_xBaseEncryptionData->m_nIterationCount = nNewCount;}
+ void setIterationCount(::std::optional<sal_Int32> const oNewCount)
+ {
+ m_xBaseEncryptionData->m_oPBKDFIterationCount = oNewCount;
+ }
+ void setArgon2Args(::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args)
+ {
+ m_xBaseEncryptionData->m_oArgon2Args = oArgon2Args;
+ }
void setSize (const sal_Int64 nNewSize);
ZipPackageStream( ZipPackage & rNewPackage,
@@ -133,7 +138,8 @@ public:
std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const css::uno::Sequence < sal_Int8 >& rEncryptionKey,
- sal_Int32 nPBKDF2IterationCount,
+ ::std::optional<sal_Int32> oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args,
const rtlRandomPool &rRandomPool ) override;
void setZipEntryOnLoading( const ZipEntry &rInEntry);
diff --git a/package/source/manifest/ManifestDefines.hxx b/package/source/manifest/ManifestDefines.hxx
index ae2095f5aab5..dbe7b985b8c0 100644
--- a/package/source/manifest/ManifestDefines.hxx
+++ b/package/source/manifest/ManifestDefines.hxx
@@ -70,6 +70,9 @@ inline constexpr OUString ELEMENT_KEY_DERIVATION = u"manifest:key-derivation"_us
inline constexpr OUString ATTRIBUTE_KEY_DERIVATION_NAME = u"manifest:key-derivation-name"_ustr;
inline constexpr OUString ATTRIBUTE_SALT = u"manifest:salt"_ustr;
inline constexpr OUString ATTRIBUTE_ITERATION_COUNT = u"manifest:iteration-count"_ustr;
+inline constexpr OUString ATTRIBUTE_ARGON2_T_LO= u"loext:argon2-iterations"_ustr;
+inline constexpr OUString ATTRIBUTE_ARGON2_M_LO= u"loext:argon2-memory"_ustr;
+inline constexpr OUString ATTRIBUTE_ARGON2_P_LO= u"loext:argon2-lanes"_ustr;
/// OFFICE-3708: wrong URL cited in ODF 1.2 and used since OOo 3.4 beta
inline constexpr OUString SHA256_URL_ODF12 = u"http://www.w3.org/2000/09/xmldsig#sha256"_ustr;
@@ -93,5 +96,7 @@ inline constexpr OUString AESGCM256_URL = u"http://www.w3.org/2009/xmlenc11#aes2
inline constexpr OUString PBKDF2_NAME = u"PBKDF2"_ustr;
inline constexpr OUString PGP_NAME = u"PGP"_ustr;
inline constexpr OUString PBKDF2_URL = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#pbkdf2"_ustr;
+inline constexpr OUString ARGON2ID_URL = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.5#argon2id"_ustr;
+inline constexpr OUString ARGON2ID_URL_LO = u"urn:org:documentfoundation:names:experimental:office:manifest:argon2id"_ustr;
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/manifest/ManifestExport.cxx b/package/source/manifest/ManifestExport.cxx
index a15ae0118277..ba6dd79b5dd0 100644
--- a/package/source/manifest/ManifestExport.cxx
+++ b/package/source/manifest/ManifestExport.cxx
@@ -20,6 +20,7 @@
#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
#include <com/sun/star/xml/crypto/DigestID.hpp>
#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/KDFID.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/uno/RuntimeException.hpp>
@@ -317,6 +318,8 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con
OUString fullPath;
OUString aString;
const uno::Any *pVector = nullptr, *pSalt = nullptr, *pIterationCount = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
+ uno::Any const* pKDF = nullptr;
+ uno::Any const* pArgon2Args = nullptr;
for (const beans::PropertyValue& rValue : rSequence)
{
if (rValue.Name == sMediaTypeProperty )
@@ -358,6 +361,11 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con
pStartKeyAlg = &rValue.Value;
else if (rValue.Name == sDerivedKeySizeProperty )
pDerivedKeySize = &rValue.Value;
+ else if (rValue.Name == "KeyDerivationFunction") {
+ pKDF = &rValue.Value;
+ } else if (rValue.Name == "Argon2Args") {
+ pArgon2Args = &rValue.Value;
+ }
}
assert(!fullPath.isEmpty());
if (isWholesomeEncryption)
@@ -367,7 +375,9 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con
xHandler->ignorableWhitespace ( sWhiteSpace );
xHandler->startElement( ELEMENT_FILE_ENTRY , pAttrList);
- if (pVector && pSalt && pIterationCount && pEncryptAlg && pStartKeyAlg && pDerivedKeySize)
+ if (pVector && pEncryptAlg && pDerivedKeySize && pKDF
+ && ((pSalt && pStartKeyAlg && (pIterationCount || pArgon2Args))
+ || pKeyInfoProperty))
{
// ==== Encryption Data
rtl::Reference<::comphelper::AttributeList> pNewAttrList = new ::comphelper::AttributeList;
@@ -487,13 +497,35 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con
if (pKeyInfoProperty)
{
+ assert(pKDF->get<sal_Int32>() == xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P);
pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME,
sPGP_Name);
}
else
{
- pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME,
- sPBKDF2_Name);
+ if (pKDF->get<sal_Int32>() == xml::crypto::KDFID::Argon2id)
+ {
+ pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME,
+ ARGON2ID_URL_LO);
+
+ uno::Sequence<sal_Int32> args;
+ *pArgon2Args >>= args;
+ assert(args.getLength() == 3);
+ pNewAttrList->AddAttribute(ATTRIBUTE_ARGON2_T_LO, OUString::number(args[0]));
+ pNewAttrList->AddAttribute(ATTRIBUTE_ARGON2_M_LO, OUString::number(args[1]));
+ pNewAttrList->AddAttribute(ATTRIBUTE_ARGON2_P_LO, OUString::number(args[2]));
+ }
+ else
+ {
+ assert(pKDF->get<sal_Int32>() == xml::crypto::KDFID::PBKDF2);
+ pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME,
+ sPBKDF2_Name);
+
+ sal_Int32 nCount = 0;
+ *pIterationCount >>= nCount;
+ aBuffer.append(nCount);
+ pNewAttrList->AddAttribute(ATTRIBUTE_ITERATION_COUNT, aBuffer.makeStringAndClear());
+ }
if (bStoreStartKeyGeneration)
{
@@ -501,11 +533,6 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con
pNewAttrList->AddAttribute ( ATTRIBUTE_KEY_SIZE, aBuffer.makeStringAndClear() );
}
- sal_Int32 nCount = 0;
- *pIterationCount >>= nCount;
- aBuffer.append(nCount);
- pNewAttrList->AddAttribute ( ATTRIBUTE_ITERATION_COUNT, aBuffer.makeStringAndClear() );
-
*pSalt >>= aSequence;
::comphelper::Base64::encode(aBuffer, aSequence);
pNewAttrList->AddAttribute ( ATTRIBUTE_SALT, aBuffer.makeStringAndClear() );
diff --git a/package/source/manifest/ManifestImport.cxx b/package/source/manifest/ManifestImport.cxx
index 0458eb9c4b8e..f0f2b8841a5e 100644
--- a/package/source/manifest/ManifestImport.cxx
+++ b/package/source/manifest/ManifestImport.cxx
@@ -25,6 +25,7 @@
#include <com/sun/star/xml/sax/XAttributeList.hpp>
#include <com/sun/star/xml/crypto/DigestID.hpp>
#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/KDFID.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <comphelper/base64.hxx>
#include <comphelper/sequence.hxx>
@@ -189,7 +190,6 @@ void ManifestImport::doAlgorithm(StringHashMap &rConvertedAttribs)
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;
@@ -234,17 +234,46 @@ void ManifestImport::doKeyDerivation(StringHashMap &rConvertedAttribs)
return;
OUString aString = rConvertedAttribs[ATTRIBUTE_KEY_DERIVATION_NAME];
- if ( aString == PBKDF2_NAME || aString == PBKDF2_URL ) {
+ if (aString == PBKDF2_NAME || aString == PBKDF2_URL
+ || aString == ARGON2ID_URL || aString == ARGON2ID_URL_LO)
+ {
+ aSequence[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
+ if (aString == ARGON2ID_URL || aString == ARGON2ID_URL_LO)
+ {
+ aSequence[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::Argon2id;
+
+ aString = rConvertedAttribs[ATTRIBUTE_ARGON2_T_LO];
+ sal_Int32 const t(aString.toInt32());
+ aString = rConvertedAttribs[ATTRIBUTE_ARGON2_M_LO];
+ sal_Int32 const m(aString.toInt32());
+ aString = rConvertedAttribs[ATTRIBUTE_ARGON2_P_LO];
+ sal_Int32 const p(aString.toInt32());
+ if (0 < t && 0 < m && 0 < p)
+ {
+ aSequence[PKG_MNFST_ARGON2ARGS].Name = "Argon2Args";
+ aSequence[PKG_MNFST_ARGON2ARGS].Value <<= uno::Sequence{t,m,p};
+ }
+ else
+ {
+ SAL_INFO("package.manifest", "invalid argon2 arguments");
+ bIgnoreEncryptData = true;
+ }
+ }
+ else
+ {
+ aSequence[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::PBKDF2;
+
+ aString = rConvertedAttribs[ATTRIBUTE_ITERATION_COUNT];
+ aSequence[PKG_MNFST_ITERATION].Name = gsIterationCountProperty;
+ aSequence[PKG_MNFST_ITERATION].Value <<= aString.toInt32();
+ }
+
aString = rConvertedAttribs[ATTRIBUTE_SALT];
uno::Sequence < sal_Int8 > aDecodeBuffer;
::comphelper::Base64::decode(aDecodeBuffer, aString);
aSequence[PKG_MNFST_SALT].Name = gsSaltProperty;
aSequence[PKG_MNFST_SALT].Value <<= aDecodeBuffer;
- aString = rConvertedAttribs[ATTRIBUTE_ITERATION_COUNT];
- aSequence[PKG_MNFST_ITERATION].Name = gsIterationCountProperty;
- aSequence[PKG_MNFST_ITERATION].Value <<= aString.toInt32();
-
aString = rConvertedAttribs[ATTRIBUTE_KEY_SIZE];
if ( aString.getLength() ) {
sal_Int32 nKey = aString.toInt32();
@@ -258,8 +287,12 @@ void ManifestImport::doKeyDerivation(StringHashMap &rConvertedAttribs)
aSequence[PKG_MNFST_DERKEYSIZE].Name = gsDerivedKeySizeProperty;
aSequence[PKG_MNFST_DERKEYSIZE].Value <<= nDerivedKeySize;
} else if ( bPgpEncryption ) {
- if ( aString != "PGP" )
+ if (aString == "PGP") {
+ aSequence[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
+ aSequence[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
+ } else {
bIgnoreEncryptData = true;
+ }
} else
bIgnoreEncryptData = true;
}
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;
diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx
index 54b8099e38d1..02f7cf71e8af 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -57,6 +57,7 @@
#include <com/sun/star/embed/StorageFormats.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/KDFID.hpp>
#include <cppuhelper/implbase.hxx>
#include <rtl/uri.hxx>
#include <rtl/random.h>
@@ -140,6 +141,7 @@ ZipPackage::ZipPackage ( uno::Reference < XComponentContext > xContext )
: m_aMutexHolder( new comphelper::RefCountedMutex )
, m_nStartKeyGenerationID( xml::crypto::DigestID::SHA1 )
, m_oChecksumDigestID( xml::crypto::DigestID::SHA1_1K )
+, m_nKeyDerivationFunctionID(xml::crypto::KDFID::PBKDF2)
, m_nCommonEncryptionID( xml::crypto::CipherID::BLOWFISH_CFB_8 )
, m_bHasEncryptedEntries ( false )
, m_bHasNonEncryptedEntries ( false )
@@ -207,6 +209,8 @@ void ZipPackage::parseManifest()
{
OUString sPath, sMediaType, sVersion;
const Any *pSalt = nullptr, *pVector = nullptr, *pCount = nullptr, *pSize = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptionAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
+ uno::Any const* pKDF = nullptr;
+ uno::Any const* pArgon2Args = nullptr;
for ( const PropertyValue& rValue : rSequence )
{
if ( rValue.Name == sPropFullPath )
@@ -235,6 +239,11 @@ void ZipPackage::parseManifest()
pDerivedKeySize = &( rValue.Value );
else if ( rValue.Name == sKeyInfo )
pKeyInfo = &( rValue.Value );
+ else if (rValue.Name == "KeyDerivationFunction") {
+ pKDF = &rValue.Value;
+ } else if (rValue.Name == "Argon2Args") {
+ pArgon2Args = &rValue.Value;
+ }
}
if ( !sPath.isEmpty() && hasByHierarchicalName ( sPath ) )
@@ -254,6 +263,7 @@ void ZipPackage::parseManifest()
if (pKeyInfo
&& pVector && pSize && pEncryptionAlg
+ && pKDF && pKDF->has<sal_Int32>() && pKDF->get<sal_Int32>() == xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P
&& ((pEncryptionAlg->has<sal_Int32>()
&& pEncryptionAlg->get<sal_Int32>() == xml::crypto::CipherID::AES_GCM_W3C)
|| (pDigest && pDigestAlg)))
@@ -289,7 +299,8 @@ void ZipPackage::parseManifest()
pStream->SetToBeCompressed ( true );
pStream->SetToBeEncrypted ( true );
pStream->SetIsEncrypted ( true );
- pStream->setIterationCount(0);
+ pStream->setIterationCount(::std::optional<sal_Int32>());
+ pStream->setArgon2Args(::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>>());
// clamp to default SHA256 start key magic value,
// c.f. ZipPackageStream::GetEncryptionKey()
@@ -303,12 +314,16 @@ void ZipPackage::parseManifest()
{
m_bHasEncryptedEntries = true;
m_oChecksumDigestID = oDigestAlg;
+ m_nKeyDerivationFunctionID = xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
m_nCommonEncryptionID = nEncryptionAlg;
m_nStartKeyGenerationID = nStartKeyAlg;
}
}
- else if (pSalt && pCount
+ else if (pSalt
&& pVector && pSize && pEncryptionAlg
+ && pKDF && pKDF->has<sal_Int32>()
+ && ((pKDF->get<sal_Int32>() == xml::crypto::KDFID::PBKDF2 && pCount)
+ || (pKDF->get<sal_Int32>() == xml::crypto::KDFID::Argon2id && pArgon2Args))
&& ((pEncryptionAlg->has<sal_Int32>()
&& pEncryptionAlg->get<sal_Int32>() == xml::crypto::CipherID::AES_GCM_W3C)
|| (pDigest && pDigestAlg)))
@@ -317,6 +332,7 @@ void ZipPackage::parseManifest()
uno::Sequence < sal_Int8 > aSequence;
sal_Int64 nSize = 0;
::std::optional<sal_Int32> oDigestAlg;
+ sal_Int32 nKDF = 0;
sal_Int32 nEncryptionAlg = 0;
sal_Int32 nCount = 0;
sal_Int32 nDerivedKeySize = 16, nStartKeyAlg = xml::crypto::DigestID::SHA1;
@@ -329,8 +345,23 @@ void ZipPackage::parseManifest()
*pVector >>= aSequence;
pStream->setInitialisationVector ( aSequence );
- *pCount >>= nCount;
- pStream->setIterationCount ( nCount );
+ *pKDF >>= nKDF;
+
+ if (pCount)
+ {
+ *pCount >>= nCount;
+ pStream->setIterationCount(::std::optional<sal_Int32>(nCount));
+ }
+
+ if (pArgon2Args)
+ {
+ uno::Sequence<sal_Int32> args;
+ *pArgon2Args >>= args;
+ assert(args.getLength() == 3);
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgs;
+ oArgs.emplace(args[0], args[1], args[2]);
+ pStream->setArgon2Args(oArgs);
+ }
*pSize >>= nSize;
pStream->setSize ( nSize );
@@ -365,6 +396,7 @@ void ZipPackage::parseManifest()
{
m_bHasEncryptedEntries = true;
m_nStartKeyGenerationID = nStartKeyAlg;
+ m_nKeyDerivationFunctionID = nKDF;
m_oChecksumDigestID = oDigestAlg;
m_nCommonEncryptionID = nEncryptionAlg;
}
@@ -1309,12 +1341,25 @@ uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
// for encrypted streams
RandomPool aRandomPool;
- // if there is only one KDF invocation, increase the safety margin
- sal_Int32 const nPBKDF2IterationCount =
- officecfg::Office::Common::Misc::ExperimentalMode::get() ? 600000 : 100000;
+ ::std::optional<sal_Int32> oPBKDF2IterationCount;
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args;
- // call saveContents ( it will recursively save sub-directories
- m_xRootFolder->saveContents("", aManList, aZipOut, GetEncryptionKey(), bIsGpgEncrypt ? 0 : nPBKDF2IterationCount, aRandomPool.get());
+ if (!bIsGpgEncrypt)
+ {
+ if (m_nKeyDerivationFunctionID == xml::crypto::KDFID::PBKDF2)
+ { // if there is only one KDF invocation, increase the safety margin
+ oPBKDF2IterationCount.emplace(officecfg::Office::Common::Misc::ExperimentalMode::get() ? 600000 : 100000);
+ }
+ else
+ {
+ assert(m_nKeyDerivationFunctionID == xml::crypto::KDFID::Argon2id);
+ oArgon2Args.emplace(3, (1<<16), 4);
+ }
+ }
+
+ // call saveContents - it will recursively save sub-directories
+ m_xRootFolder->saveContents("", aManList, aZipOut, GetEncryptionKey(),
+ oPBKDF2IterationCount, oArgon2Args, aRandomPool.get());
}
if( m_nFormat == embed::StorageFormats::PACKAGE )
@@ -1757,6 +1802,18 @@ void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const
m_nStartKeyGenerationID = nID;
}
+ else if (rAlgorithm.Name == "KeyDerivationFunction")
+ {
+ sal_Int32 nID = 0;
+ if (!(rAlgorithm.Value >>= nID)
+ || (nID != xml::crypto::KDFID::PBKDF2
+ && nID != xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P
+ && nID != xml::crypto::KDFID::Argon2id))
+ {
+ throw IllegalArgumentException(THROW_WHERE "Unexpected key derivation function provided!", uno::Reference<uno::XInterface>(), 2);
+ }
+ m_nKeyDerivationFunctionID = nID;
+ }
else if ( rAlgorithm.Name == "EncryptionAlgorithm" )
{
sal_Int32 nID = 0;
@@ -1807,6 +1864,7 @@ void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const
// defaults) with reasonable values
// note: these should be overridden by SfxObjectShell::SetupStorage()
m_nStartKeyGenerationID = 0; // this is unused for PGP
+ m_nKeyDerivationFunctionID = xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
m_nCommonEncryptionID = xml::crypto::CipherID::AES_CBC_W3C_PADDING;
m_oChecksumDigestID.emplace(xml::crypto::DigestID::SHA512_1K);
}
@@ -1828,6 +1886,7 @@ Any SAL_CALL ZipPackage::getPropertyValue( const OUString& PropertyName )
{
::comphelper::SequenceAsHashMap aAlgorithms;
aAlgorithms["StartKeyGenerationAlgorithm"] <<= m_nStartKeyGenerationID;
+ aAlgorithms["KeyDerivationFunction"] <<= m_nKeyDerivationFunctionID;
aAlgorithms["EncryptionAlgorithm"] <<= m_nCommonEncryptionID;
if (m_oChecksumDigestID)
{
diff --git a/package/source/zippackage/ZipPackageFolder.cxx b/package/source/zippackage/ZipPackageFolder.cxx
index 21c71b14cf09..bca4e46e1bc2 100644
--- a/package/source/zippackage/ZipPackageFolder.cxx
+++ b/package/source/zippackage/ZipPackageFolder.cxx
@@ -231,7 +231,8 @@ bool ZipPackageFolder::saveChild(
std::vector < uno::Sequence < PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const uno::Sequence < sal_Int8 >& rEncryptionKey,
- sal_Int32 nPBKDF2IterationCount,
+ ::std::optional<sal_Int32> const oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args,
const rtlRandomPool &rRandomPool)
{
uno::Sequence < PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST);
@@ -250,7 +251,7 @@ bool ZipPackageFolder::saveChild(
else
aPropSet.realloc( 0 );
- saveContents( sTempName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool);
+ saveContents(sTempName, rManList, rZipOut, rEncryptionKey, oPBKDF2IterationCount, oArgon2Args, rRandomPool);
// folder can have a mediatype only in package format
if ( aPropSet.hasElements() && ( m_nFormat == embed::StorageFormats::PACKAGE ) )
@@ -264,7 +265,8 @@ void ZipPackageFolder::saveContents(
std::vector < uno::Sequence < PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const uno::Sequence < sal_Int8 >& rEncryptionKey,
- sal_Int32 nPBKDF2IterationCount,
+ ::std::optional<sal_Int32> const oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args,
const rtlRandomPool &rRandomPool ) const
{
if ( maContents.empty() && !rPath.isEmpty() && m_nFormat != embed::StorageFormats::OFOPXML )
@@ -300,8 +302,8 @@ void ZipPackageFolder::saveContents(
if ( aIter != maContents.end() && !(*aIter).second.bFolder )
{
bMimeTypeStreamStored = true;
- if( !aIter->second.pStream->saveChild(
- rPath + aIter->first, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
+ if (!aIter->second.pStream->saveChild(rPath + aIter->first, rManList, rZipOut,
+ rEncryptionKey, oPBKDF2IterationCount, oArgon2Args, rRandomPool))
{
throw uno::RuntimeException( THROW_WHERE );
}
@@ -314,16 +316,16 @@ void ZipPackageFolder::saveContents(
{
if (rInfo.bFolder)
{
- if( !rInfo.pFolder->saveChild(
- rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
+ if (!rInfo.pFolder->saveChild(rPath + rShortName, rManList, rZipOut,
+ rEncryptionKey, oPBKDF2IterationCount, oArgon2Args, rRandomPool))
{
throw uno::RuntimeException( THROW_WHERE );
}
}
else
{
- if( !rInfo.pStream->saveChild(
- rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
+ if (!rInfo.pStream->saveChild(rPath + rShortName, rManList, rZipOut,
+ rEncryptionKey, oPBKDF2IterationCount, oArgon2Args, rRandomPool))
{
throw uno::RuntimeException( THROW_WHERE );
}
diff --git a/package/source/zippackage/ZipPackageStream.cxx b/package/source/zippackage/ZipPackageStream.cxx
index 6ef2241d464a..d3068a666519 100644
--- a/package/source/zippackage/ZipPackageStream.cxx
+++ b/package/source/zippackage/ZipPackageStream.cxx
@@ -35,6 +35,7 @@
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/xml/crypto/DigestID.hpp>
#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/KDFID.hpp>
#include <CRC32.hxx>
#include <ZipOutputEntry.hxx>
@@ -450,7 +451,8 @@ bool ZipPackageStream::saveChild(
std::vector < uno::Sequence < beans::PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const uno::Sequence < sal_Int8 >& rEncryptionKey,
- sal_Int32 nPBKDF2IterationCount,
+ ::std::optional<sal_Int32> const oPBKDF2IterationCount,
+ ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args,
const rtlRandomPool &rRandomPool)
{
bool bSuccess = true;
@@ -600,7 +602,8 @@ bool ZipPackageStream::saveChild(
setInitialisationVector ( aVector );
setSalt ( aSalt );
- setIterationCount(nPBKDF2IterationCount);
+ setIterationCount(oPBKDF2IterationCount);
+ setArgon2Args(oArgon2Args);
}
// last property is digest, which is inserted later if we didn't have
@@ -611,8 +614,29 @@ bool ZipPackageStream::saveChild(
pPropSet[PKG_MNFST_INIVECTOR].Value <<= m_xBaseEncryptionData->m_aInitVector;
pPropSet[PKG_MNFST_SALT].Name = "Salt";
pPropSet[PKG_MNFST_SALT].Value <<= m_xBaseEncryptionData->m_aSalt;
- pPropSet[PKG_MNFST_ITERATION].Name = "IterationCount";
- pPropSet[PKG_MNFST_ITERATION].Value <<= m_xBaseEncryptionData->m_nIterationCount;
+ if (m_xBaseEncryptionData->m_oArgon2Args)
+ {
+ pPropSet[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
+ pPropSet[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::Argon2id;
+ pPropSet[PKG_MNFST_ARGON2ARGS].Name = "Argon2Args";
+ uno::Sequence<sal_Int32> const args{
+ ::std::get<0>(*m_xBaseEncryptionData->m_oArgon2Args),
+ ::std::get<1>(*m_xBaseEncryptionData->m_oArgon2Args),
+ ::std::get<2>(*m_xBaseEncryptionData->m_oArgon2Args) };
+ pPropSet[PKG_MNFST_ARGON2ARGS].Value <<= args;
+ }
+ else if (m_xBaseEncryptionData->m_oPBKDFIterationCount)
+ {
+ pPropSet[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
+ pPropSet[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::PBKDF2;
+ pPropSet[PKG_MNFST_ITERATION].Name = "IterationCount";
+ pPropSet[PKG_MNFST_ITERATION].Value <<= *m_xBaseEncryptionData->m_oPBKDFIterationCount;
+ }
+ else
+ {
+ pPropSet[PKG_MNFST_KDF].Name = "KeyDerivationFunction";
+ pPropSet[PKG_MNFST_KDF].Value <<= xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
+ }
// Need to store the uncompressed size in the manifest
OSL_ENSURE( m_nOwnStreamOrigSize >= 0, "The stream size was not correctly initialized!" );
@@ -625,19 +649,16 @@ bool ZipPackageStream::saveChild(
if ( !xEncData.is() )
throw uno::RuntimeException();
- pPropSet[PKG_MNFST_DIGEST].Name = sDigestProperty;
- if (xEncData->m_oCheckAlg)
- {
- pPropSet[PKG_MNFST_DIGEST].Value <<= m_xBaseEncryptionData->m_aDigest;
- }
pPropSet[PKG_MNFST_ENCALG].Name = sEncryptionAlgProperty;
pPropSet[PKG_MNFST_ENCALG].Value <<= xEncData->m_nEncAlg;
pPropSet[PKG_MNFST_STARTALG].Name = sStartKeyAlgProperty;
pPropSet[PKG_MNFST_STARTALG].Value <<= xEncData->m_nStartKeyGenID;
- pPropSet[PKG_MNFST_DIGESTALG].Name = sDigestAlgProperty;
if (xEncData->m_oCheckAlg)
{
assert(xEncData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C);
+ pPropSet[PKG_MNFST_DIGEST].Name = sDigestProperty;
+ pPropSet[PKG_MNFST_DIGEST].Value <<= m_xBaseEncryptionData->m_aDigest;
+ pPropSet[PKG_MNFST_DIGESTALG].Name = sDigestAlgProperty;
pPropSet[PKG_MNFST_DIGESTALG].Value <<= *xEncData->m_oCheckAlg;
}
pPropSet[PKG_MNFST_DERKEYSIZE].Name = sDerivedKeySizeProperty;
@@ -823,19 +844,22 @@ bool ZipPackageStream::saveChild(
if ( !xEncData.is() )
throw uno::RuntimeException();
- pPropSet[PKG_MNFST_DIGEST].Name = sDigestProperty;
- if (xEncData->m_oCheckAlg)
- {
- pPropSet[PKG_MNFST_DIGEST].Value <<= m_xBaseEncryptionData->m_aDigest;
- }
+ // very confusing: half the encryption properties are
+ // unconditionally added above and the other half conditionally;
+ // assert that we have the expected group and not duplicates
+ assert(std::any_of(aPropSet.begin(), aPropSet.end(), [](auto const& it){ return it.Name == "Salt"; }));
+ assert(!std::any_of(aPropSet.begin(), aPropSet.end(), [](auto const& it){ return it.Name == sEncryptionAlgProperty; }));
+
pPropSet[PKG_MNFST_ENCALG].Name = sEncryptionAlgProperty;
pPropSet[PKG_MNFST_ENCALG].Value <<= xEncData->m_nEncAlg;
pPropSet[PKG_MNFST_STARTALG].Name = sStartKeyAlgProperty;
pPropSet[PKG_MNFST_STARTALG].Value <<= xEncData->m_nStartKeyGenID;
- pPropSet[PKG_MNFST_DIGESTALG].Name = sDigestAlgProperty;
if (xEncData->m_oCheckAlg)
{
assert(xEncData->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C);
+ pPropSet[PKG_MNFST_DIGEST].Name = sDigestProperty;
+ pPropSet[PKG_MNFST_DIGEST].Value <<= m_xBaseEncryptionData->m_aDigest;
+ pPropSet[PKG_MNFST_DIGESTALG].Name = sDigestAlgProperty;
pPropSet[PKG_MNFST_DIGESTALG].Value <<= *xEncData->m_oCheckAlg;
}
pPropSet[PKG_MNFST_DERKEYSIZE].Name = sDerivedKeySizeProperty;
diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx
index 40449b004713..ea1063ea81c1 100644
--- a/sfx2/source/doc/objstor.cxx
+++ b/sfx2/source/doc/objstor.cxx
@@ -58,6 +58,7 @@
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/xml/crypto/CipherID.hpp>
#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/KDFID.hpp>
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
@@ -336,7 +337,8 @@ void SfxObjectShell::SetupStorage( const uno::Reference< embed::XStorage >& xSto
{
{ "StartKeyGenerationAlgorithm", css::uno::Any(xml::crypto::DigestID::SHA1) },
{ "EncryptionAlgorithm", css::uno::Any(xml::crypto::CipherID::BLOWFISH_CFB_8) },
- { "ChecksumAlgorithm", css::uno::Any(xml::crypto::DigestID::SHA1_1K) }
+ { "ChecksumAlgorithm", css::uno::Any(xml::crypto::DigestID::SHA1_1K) },
+ { "KeyDerivationFunction", css::uno::Any(xml::crypto::KDFID::PBKDF2) },
};
if (nDefVersion >= SvtSaveOptions::ODFSVER_012)
@@ -367,6 +369,10 @@ void SfxObjectShell::SetupStorage( const uno::Reference< embed::XStorage >& xSto
{
pEncryptionAlgs[1].Value <<= xml::crypto::CipherID::AES_GCM_W3C;
pEncryptionAlgs[2].Value.clear();
+ if (!getenv("LO_ARGON2_DISABLE"))
+ {
+ pEncryptionAlgs[3].Value <<= xml::crypto::KDFID::Argon2id;
+ }
}
else
{