From 2f512aaa6c39390a5a0eb1d1e37f070127d068a4 Mon Sep 17 00:00:00 2001 From: Michael Stahl Date: Tue, 19 Dec 2023 19:13:00 +0100 Subject: [PATCH] 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 --- bin/lo-all-static-libs | 1 + ...piler-error-due-to-undefined-_MSC_VE.patch | 32 +++++++ external/argon2/UnpackedTarball_argon2.mk | 6 ++ offapi/UnoApi_offapi.mk | 1 + .../embed/XEncryptionProtectedStorage.idl | 8 ++ offapi/com/sun/star/xml/crypto/KDFID.idl | 47 ++++++++++ package/Library_package2.mk | 1 + package/inc/EncryptedDataHeader.hxx | 9 +- package/inc/EncryptionData.hxx | 10 +- package/inc/PackageConstants.hxx | 5 +- package/inc/ZipPackage.hxx | 1 + package/inc/ZipPackageEntry.hxx | 5 +- package/inc/ZipPackageFolder.hxx | 6 +- package/inc/ZipPackageStream.hxx | 14 ++- package/source/manifest/ManifestDefines.hxx | 5 + package/source/manifest/ManifestExport.cxx | 43 +++++++-- package/source/manifest/ManifestImport.cxx | 47 ++++++++-- package/source/zipapi/XUnbufferedStream.cxx | 2 +- package/source/zipapi/ZipFile.cxx | 94 ++++++++++++++++++- package/source/zippackage/ZipPackage.cxx | 77 +++++++++++++-- .../source/zippackage/ZipPackageFolder.cxx | 20 ++-- .../source/zippackage/ZipPackageStream.cxx | 56 +++++++---- sfx2/source/doc/objstor.cxx | 8 +- 23 files changed, 428 insertions(+), 70 deletions(-) create mode 100644 external/argon2/0001-Fix-possible-compiler-error-due-to-undefined-_MSC_VE.patch create mode 100644 offapi/com/sun/star/xml/crypto/KDFID.idl 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 +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. +
KeyDerivationFunction
+
+ 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. +
EncryptionAlgorithm
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 #include +#include 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 m_oPBKDFIterationCount; + ::std::optional<::std::tuple> 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 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 #include +#include +#include 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 oPBKDF2IterationCount, + ::std::optional<::std::tuple> 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 oPBKDF2IterationCount, + ::std::optional<::std::tuple> 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 oPBKDF2IterationCount, + ::std::optional<::std::tuple> 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 #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 const oNewCount) + { + m_xBaseEncryptionData->m_oPBKDFIterationCount = oNewCount; + } + void setArgon2Args(::std::optional<::std::tuple> 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 oPBKDF2IterationCount, + ::std::optional<::std::tuple> 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 #include #include +#include #include #include #include @@ -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() == 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() == xml::crypto::KDFID::Argon2id) + { + pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME, + ARGON2ID_URL_LO); + + uno::Sequence 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() == 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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -44,6 +45,8 @@ #include #include +#include + #include "blowfishcontext.hxx" #include "sha1context.hxx" #include @@ -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(aDerivedKey.getArray()), + .outlen = ::sal::static_int_cast(aDerivedKey.getLength()), + .pwd = reinterpret_cast(xEncryptionData->m_aKey.getArray()), + .pwdlen = ::sal::static_int_cast(xEncryptionData->m_aKey.getLength()), + .salt = reinterpret_cast(xEncryptionData->m_aSalt.getArray()), + .saltlen = ::sal::static_int_cast(xEncryptionData->m_aSalt.getLength()), + .secret = nullptr, .secretlen = 0, + .ad = nullptr, .adlen = 0, + .t_cost = ::sal::static_int_cast(::std::get<0>(*xEncryptionData->m_oArgon2Args)), + .m_cost = ::sal::static_int_cast(::std::get<1>(*xEncryptionData->m_oArgon2Args)), + .lanes = ::sal::static_int_cast(::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((nArgon2t >> 0) & 0xFF); + *(pHeader++) = static_cast((nArgon2t >> 8) & 0xFF); + *(pHeader++) = static_cast((nArgon2t >> 16) & 0xFF); + *(pHeader++) = static_cast((nArgon2t >> 24) & 0xFF); + + sal_Int32 const nArgon2m = rData->m_oArgon2Args ? ::std::get<1>(*rData->m_oArgon2Args) : 0; + *(pHeader++) = static_cast((nArgon2m >> 0) & 0xFF); + *(pHeader++) = static_cast((nArgon2m >> 8) & 0xFF); + *(pHeader++) = static_cast((nArgon2m >> 16) & 0xFF); + *(pHeader++) = static_cast((nArgon2m >> 24) & 0xFF); + + sal_Int32 const nArgon2p = rData->m_oArgon2Args ? ::std::get<2>(*rData->m_oArgon2Args) : 0; + *(pHeader++) = static_cast((nArgon2p >> 0) & 0xFF); + *(pHeader++) = static_cast((nArgon2p >> 8) & 0xFF); + *(pHeader++) = static_cast((nArgon2p >> 16) & 0xFF); + *(pHeader++) = static_cast((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 #include #include +#include #include #include #include @@ -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() && pKDF->get() == xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P && ((pEncryptionAlg->has() && pEncryptionAlg->get() == 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()); + pStream->setArgon2Args(::std::optional<::std::tuple>()); // 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() + && ((pKDF->get() == xml::crypto::KDFID::PBKDF2 && pCount) + || (pKDF->get() == xml::crypto::KDFID::Argon2id && pArgon2Args)) && ((pEncryptionAlg->has() && pEncryptionAlg->get() == 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 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(nCount)); + } + + if (pArgon2Args) + { + uno::Sequence args; + *pArgon2Args >>= args; + assert(args.getLength() == 3); + ::std::optional<::std::tuple> 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 oPBKDF2IterationCount; + ::std::optional<::std::tuple> 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(), 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 const oPBKDF2IterationCount, + ::std::optional<::std::tuple> 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 const oPBKDF2IterationCount, + ::std::optional<::std::tuple> 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 #include #include +#include #include #include @@ -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 const oPBKDF2IterationCount, + ::std::optional<::std::tuple> 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 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 #include #include +#include #include #include @@ -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 {