comphelper/source/misc/docpasswordhelper.cxx | 2 comphelper/source/misc/storagehelper.cxx | 13 ++ include/comphelper/storagehelper.hxx | 1 include/rtl/digest.h | 63 +++++++++++++ include/test/testinteractionhandler.hxx | 11 ++ officecfg/registry/schema/org/openoffice/Office/Common.xcs | 15 --- package/inc/EncryptionData.hxx | 5 - package/inc/ZipPackageStream.hxx | 5 - package/source/zipapi/ZipFile.cxx | 11 ++ package/source/zipapi/sha1context.cxx | 52 +++++++++- package/source/zipapi/sha1context.hxx | 26 ++++- package/source/zippackage/ZipPackage.cxx | 2 package/source/zippackage/ZipPackageStream.cxx | 32 ++++-- sfx2/source/dialog/filedlghelper.cxx | 21 ++++ sfx2/source/doc/objstor.cxx | 15 --- sw/qa/extras/inc/swmodeltestbase.hxx | 17 ++- sw/qa/extras/odfexport/data/sha1_correct.odt |binary sw/qa/extras/odfexport/data/sha1_wrong.odt |binary sw/qa/extras/odfexport/odfexport.cxx | 10 ++ uui/source/iahndl-authentication.cxx | 3 20 files changed, 248 insertions(+), 56 deletions(-)
New commits: commit efc06e9bb696110350ab3e14344de53db992280e Author: Michael Stahl <[email protected]> Date: Fri Jan 12 22:44:43 2018 +0100 tdf#114939 sal: deprecate rtl_digest_*SHA* and rtl_digest_PBKDF2 Document the disaster but don't fix it so this implementation can be used to import existing documents with bad SHA1 hashes. Change-Id: I4f3648dd0987392ef49dc149a9213bcafcefb202 diff --git a/include/rtl/digest.h b/include/rtl/digest.h index 7e225e18d0ea..6652038874fe 100644 --- a/include/rtl/digest.h +++ b/include/rtl/digest.h @@ -288,11 +288,18 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_MD5 ( FIPS PUB 180 (Superseded by FIPS PUB 180-1) Secure Hash Standard + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_create() */ SAL_DLLPUBLIC rtlDigest SAL_CALL rtl_digest_createSHA (void) SAL_THROW_EXTERN_C(); /** Destroy a SHA digest handle. + + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_destroy() */ SAL_DLLPUBLIC void SAL_CALL rtl_digest_destroySHA ( @@ -301,6 +308,10 @@ SAL_DLLPUBLIC void SAL_CALL rtl_digest_destroySHA ( /** Update a SHA digest with given data. + + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_update() */ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_updateSHA ( @@ -309,6 +320,10 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_updateSHA ( ) SAL_THROW_EXTERN_C(); /** Finalize a SHA digest and retrieve the digest value. + + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_get() */ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_getSHA ( @@ -321,6 +336,9 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_getSHA ( This function performs an optimized call sequence on a single data buffer, avoiding digest creation and destruction. + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_updateSHA() @see rtl_digest_getSHA() @@ -349,11 +367,18 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_SHA ( FIPS PUB 180-1 (Supersedes FIPS PUB 180) Secure Hash Standard + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_create() */ SAL_DLLPUBLIC rtlDigest SAL_CALL rtl_digest_createSHA1 (void) SAL_THROW_EXTERN_C(); /** Destroy a SHA1 digest handle. + + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_destroy() */ SAL_DLLPUBLIC void SAL_CALL rtl_digest_destroySHA1 ( @@ -361,6 +386,10 @@ SAL_DLLPUBLIC void SAL_CALL rtl_digest_destroySHA1 ( ) SAL_THROW_EXTERN_C(); /** Update a SHA1 digest with given data. + + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_update() */ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_updateSHA1 ( @@ -369,6 +398,10 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_updateSHA1 ( ) SAL_THROW_EXTERN_C(); /** Finalize a SHA1 digest and retrieve the digest value. + + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_get() */ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_getSHA1 ( @@ -381,6 +414,9 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_getSHA1 ( This function performs an optimized call sequence on a single data buffer, avoiding digest creation and destruction. + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_updateSHA1() @see rtl_digest_getSHA1() @@ -480,11 +516,18 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_HMAC_MD5 ( RFC 2898 (Informational) PKCS #5: Password-Based Cryptography Specification Version 2.0 + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_create() */ SAL_DLLPUBLIC rtlDigest SAL_CALL rtl_digest_createHMAC_SHA1 (void) SAL_THROW_EXTERN_C(); /** Destroy a HMAC_SHA1 digest handle. + + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_destroy() */ SAL_DLLPUBLIC void SAL_CALL rtl_digest_destroyHMAC_SHA1 ( @@ -492,6 +535,10 @@ SAL_DLLPUBLIC void SAL_CALL rtl_digest_destroyHMAC_SHA1 ( ) SAL_THROW_EXTERN_C(); /** Initialize a HMAC_SHA1 digest. + + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_init() @param Digest [in] digest handle. @@ -506,6 +553,10 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_initHMAC_SHA1 ( ) SAL_THROW_EXTERN_C(); /** Update a HMAC_SHA1 digest with given data. + + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_update() */ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_updateHMAC_SHA1 ( @@ -514,6 +565,10 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_updateHMAC_SHA1 ( ) SAL_THROW_EXTERN_C(); /** Finalize a HMAC_SHA1 digest and retrieve the digest value. + + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_get() */ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_getHMAC_SHA1 ( @@ -526,6 +581,9 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_getHMAC_SHA1 ( This function performs an optimized call sequence on a single data buffer, avoiding digest creation and destruction. + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility. + @see rtl_digest_initHMAC_SHA1() @see rtl_digest_updateHMAC_SHA1() @see rtl_digest_getHMAC_SHA1() @@ -551,6 +609,11 @@ SAL_DLLPUBLIC rtlDigestError SAL_CALL rtl_digest_HMAC_SHA1 ( RFC 2898 (Informational) PKCS #5: Password-Based Cryptography Specification Version 2.0 + @deprecated The implementation is buggy and generates incorrect results + for 52 <= (len % 64) <= 55; use only for bug-compatibility + or if the input is guaranteed to have a good length + by a start-key derivation round. + @param pKeyData [out] derived key @param nKeyLen [in] derived key length @param pPassData [in] password commit 9ef1734f03a008545a01fd394dd0e979bb230a0f Author: Michael Stahl <[email protected]> Date: Fri Jan 12 22:30:07 2018 +0100 tdf#114939 sfx2: notify user of non-interoperable passwords For ODF 1.1 encryption, SHA1 is used as the start-key digest algorithm of PBKDF2. For the ODF export password-to-modify "feature", PBKDF2 is used without a start-key digest round, so the UTF-8 encoded password is directly the input. In both cases, if the user entered a password with a known problematic length, reject it and request a new one. Change-Id: Ie6510c1e668e1ed32be439ebe00354881b6ca83f diff --git a/comphelper/source/misc/docpasswordhelper.cxx b/comphelper/source/misc/docpasswordhelper.cxx index 9cf2fdffa0a8..13ab45f043af 100644 --- a/comphelper/source/misc/docpasswordhelper.cxx +++ b/comphelper/source/misc/docpasswordhelper.cxx @@ -49,6 +49,8 @@ static uno::Sequence< sal_Int8 > GeneratePBKDF2Hash( const OUString& aPassword, if ( !aPassword.isEmpty() && aSalt.getLength() && nCount && nHashLength ) { OString aBytePass = OUStringToOString( aPassword, RTL_TEXTENCODING_UTF8 ); + // FIXME this is subject to the SHA1-bug tdf#114939 - see also + // RequestPassword() in filedlghelper.cxx aResult.realloc( 16 ); rtl_digest_PBKDF2( reinterpret_cast < sal_uInt8 * > ( aResult.getArray() ), aResult.getLength(), diff --git a/sfx2/source/dialog/filedlghelper.cxx b/sfx2/source/dialog/filedlghelper.cxx index 9871ab45cc3e..f19ea1488b63 100644 --- a/sfx2/source/dialog/filedlghelper.cxx +++ b/sfx2/source/dialog/filedlghelper.cxx @@ -60,6 +60,7 @@ #include <vcl/msgbox.hxx> #include <vcl/mnemonic.hxx> #include <unotools/pathoptions.hxx> +#include <unotools/saveopt.hxx> #include <unotools/securityoptions.hxx> #include <svl/itemset.hxx> #include <svl/eitem.hxx> @@ -2668,7 +2669,25 @@ ErrCode RequestPassword(const std::shared_ptr<const SfxFilter>& pCurrentFilter, ::rtl::Reference< ::comphelper::DocPasswordRequest > pPasswordRequest( new ::comphelper::DocPasswordRequest( eType, css::task::PasswordRequestMode_PASSWORD_CREATE, aURL, bool( pCurrentFilter->GetFilterFlags() & SfxFilterFlags::PASSWORDTOMODIFY ) ) ); uno::Reference< css::task::XInteractionRequest > rRequest( pPasswordRequest.get() ); - xInteractionHandler->handle( rRequest ); + do { + xInteractionHandler->handle( rRequest ); + if (pPasswordRequest->isPassword() && !bMSType) + { + OString const utf8Pwd(OUStringToOString(pPasswordRequest->getPassword(), RTL_TEXTENCODING_UTF8)); + OString const utf8Ptm(OUStringToOString(pPasswordRequest->getPasswordToModify(), RTL_TEXTENCODING_UTF8)); + if (!(52 <= utf8Pwd.getLength() && utf8Pwd.getLength() <= 55 + && SvtSaveOptions().GetODFDefaultVersion() < SvtSaveOptions::ODFVER_012) + && !(52 <= utf8Ptm.getLength() && utf8Ptm.getLength() <= 55)) + { + break; + } + ScopedVclPtrInstance<MessBox>(Application::GetDefDialogParent(), + MessBoxStyle::Ok, 0, "Password length", + "The password you have entered causes interoperability issues. Please enter a password that is shorter than 52 bytes, or longer than 55 bytes." + )->Execute(); + + } + } while (true); if ( pPasswordRequest->isPassword() ) { if ( pPasswordRequest->getPassword().getLength() ) diff --git a/uui/source/iahndl-authentication.cxx b/uui/source/iahndl-authentication.cxx index 643e0e12a4e2..14f4425958b7 100644 --- a/uui/source/iahndl-authentication.cxx +++ b/uui/source/iahndl-authentication.cxx @@ -438,6 +438,9 @@ executeMasterPasswordDialog( } sal_uInt8 aKey[RTL_DIGEST_LENGTH_MD5]; + // FIXME this is subject to the SHA1-bug tdf#114939 - but this + // MasterPassword stuff is just stored in the UserInstallation, + // so no interop concerns rtl_digest_PBKDF2(aKey, RTL_DIGEST_LENGTH_MD5, reinterpret_cast< sal_uInt8 const * >(aMaster.getStr()), commit 50382b9e9256d7361e3770daa654fb8d09448635 Author: Michael Stahl <[email protected]> Date: Fri Jan 12 18:34:04 2018 +0100 tdf#114939 package: change ODF 1.1 export to use real SHA1 Change-Id: I20d4965cc467df56536ca03b773b3f0f61b1f2a3 diff --git a/package/inc/ZipPackageStream.hxx b/package/inc/ZipPackageStream.hxx index 67428c086948..b691370bf7e0 100644 --- a/package/inc/ZipPackageStream.hxx +++ b/package/inc/ZipPackageStream.hxx @@ -84,9 +84,9 @@ public: void SetFromManifest( bool bValue ) { m_bFromManifest = bValue; } enum class Bugs { None, WinEncodingWrongSHA1, WrongSHA1 }; - ::rtl::Reference<EncryptionData> GetEncryptionData(Bugs bugs = Bugs::WrongSHA1); + ::rtl::Reference<EncryptionData> GetEncryptionData(Bugs bugs = Bugs::None); - css::uno::Sequence<sal_Int8> GetEncryptionKey(Bugs bugs = Bugs::WrongSHA1); + css::uno::Sequence<sal_Int8> GetEncryptionKey(Bugs bugs = Bugs::None); sal_Int32 GetStartKeyGenID(); diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx index ffb9cf1e8775..a6eb5f6af97e 100644 --- a/package/source/zippackage/ZipPackage.cxx +++ b/package/source/zippackage/ZipPackage.cxx @@ -1574,7 +1574,7 @@ const uno::Sequence< sal_Int8 > ZipPackage::GetEncryptionKey() if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA256 ) aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA256UTF8; else if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA1 ) - aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA1UTF8; + aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT; else throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" ); diff --git a/package/source/zippackage/ZipPackageStream.cxx b/package/source/zippackage/ZipPackageStream.cxx index c9e987aec1bd..55ebae82ee48 100644 --- a/package/source/zippackage/ZipPackageStream.cxx +++ b/package/source/zippackage/ZipPackageStream.cxx @@ -1012,7 +1012,7 @@ uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getDataStream() uno::Reference< io::XInputStream > xResult; try { - xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::WrongSHA1), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() ); + xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::None), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() ); } catch( const packages::WrongPasswordException& ) { @@ -1020,8 +1020,8 @@ uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getDataStream() { SAL_WARN("package", "ZipPackageStream::getDataStream(): SHA1 mismatch, trying fallbacks..."); try - { // tdf#114939 try without legacy StarOffice SHA1 bug - xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::None), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() ); + { // tdf#114939 try with legacy StarOffice SHA1 bug + xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::WrongSHA1), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() ); return xResult; } catch (const packages::WrongPasswordException&) commit 9188ea83c346fdc2f668178ae7538665a1b09c02 Author: Michael Stahl <[email protected]> Date: Fri Jan 12 16:58:00 2018 +0100 tdf#114939 package,comphelper: Try both real SHA1 and StarOffice SHA1 ... when importing ODF documents. In CreatePackageEncryptionData(), add a 3rd SHA1 password hash, PackageSHA1CorrectEncryptionKey, to EncryptionData. Use it in ZipPackageStream::getDataStream(), which has 3 fall-backs for SHA1 bugs now. Also add a CorrectSHA1DigestContext, to be used together with PackageSHA1CorrectEncryptionKey, and rename the existing one to StarOfficeSHA1DigestContext, to be used together with the existing 2 PackageSHA1{UTF8,MS1252}EncryptionKey. The fallback won't be used very often anyway: for the password SHA1 to be wrong, you need a password between 52 and 55 bytes long, and for the SHA1/1K checksum to be wrong, you need a file smaller than 1K with compressed size mod 64 between 52 and 55; all XML files have enough random "chaff" added to be too large. Test that we can read both correct SHA1 and StarOffice SHA1. Change-Id: I988fa489b5e40c7657f404f18538f637d54d28f1 diff --git a/comphelper/source/misc/storagehelper.cxx b/comphelper/source/misc/storagehelper.cxx index de813848653b..dd52b7f6181a 100644 --- a/comphelper/source/misc/storagehelper.cxx +++ b/comphelper/source/misc/storagehelper.cxx @@ -50,6 +50,7 @@ #include <ucbhelper/content.hxx> #include <comphelper/fileformat.h> +#include <comphelper/hash.hxx> #include <comphelper/processfactory.hxx> #include <comphelper/documentconstants.hxx> #include <comphelper/storagehelper.hxx> @@ -400,7 +401,8 @@ uno::Sequence< beans::NamedValue > OStorageHelper::CreatePackageEncryptionData( // MS_1252 encoding was used for SO60 document format password encoding, // this encoding supports only a minor subset of nonascii characters, // but for compatibility reasons it has to be used for old document formats - aEncryptionData.realloc( nSha1Ind + 2 ); + aEncryptionData.realloc( nSha1Ind + 3 ); + // these are StarOffice not-quite-SHA1 aEncryptionData[nSha1Ind].Name = PACKAGE_ENCRYPTIONDATA_SHA1UTF8; aEncryptionData[nSha1Ind + 1].Name = PACKAGE_ENCRYPTIONDATA_SHA1MS1252; @@ -424,6 +426,15 @@ uno::Sequence< beans::NamedValue > OStorageHelper::CreatePackageEncryptionData( aEncryptionData[nSha1Ind+nInd].Value <<= uno::Sequence< sal_Int8 >( reinterpret_cast<sal_Int8*>(pBuffer), RTL_DIGEST_LENGTH_SHA1 ); } + + // actual SHA1 + aEncryptionData[nSha1Ind + 2].Name = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT; + OString aByteStrPass = OUStringToOString(aPassword, RTL_TEXTENCODING_UTF8); + std::vector<unsigned char> const sha1(::comphelper::Hash::calculateHash( + reinterpret_cast<unsigned char const*>(aByteStrPass.getStr()), aByteStrPass.getLength(), + ::comphelper::HashType::SHA1)); + aEncryptionData[nSha1Ind + 2].Value <<= uno::Sequence<sal_Int8>( + reinterpret_cast<sal_Int8 const*>(sha1.data()), sha1.size()); } return aEncryptionData; diff --git a/include/comphelper/storagehelper.hxx b/include/comphelper/storagehelper.hxx index 1f5e22cb14e3..778fb1c8ea25 100644 --- a/include/comphelper/storagehelper.hxx +++ b/include/comphelper/storagehelper.hxx @@ -32,6 +32,7 @@ #define PACKAGE_ENCRYPTIONDATA_SHA256UTF8 "PackageSHA256UTF8EncryptionKey" #define PACKAGE_ENCRYPTIONDATA_SHA1UTF8 "PackageSHA1UTF8EncryptionKey" #define PACKAGE_ENCRYPTIONDATA_SHA1MS1252 "PackageSHA1MS1252EncryptionKey" +#define PACKAGE_ENCRYPTIONDATA_SHA1CORRECT "PackageSHA1CorrectEncryptionKey" namespace com { namespace sun { namespace star { namespace beans { struct NamedValue; } diff --git a/package/inc/EncryptionData.hxx b/package/inc/EncryptionData.hxx index 0add43f143de..c7c6ffb3555e 100644 --- a/package/inc/EncryptionData.hxx +++ b/package/inc/EncryptionData.hxx @@ -50,14 +50,16 @@ public: sal_Int32 m_nCheckAlg; sal_Int32 m_nDerivedKeySize; sal_Int32 m_nStartKeyGenID; + bool m_bTryWrongSHA1; - EncryptionData( const BaseEncryptionData& aData, const css::uno::Sequence< sal_Int8 >& aKey, sal_Int32 nEncAlg, sal_Int32 nCheckAlg, sal_Int32 nDerivedKeySize, sal_Int32 nStartKeyGenID ) + EncryptionData(const BaseEncryptionData& aData, const css::uno::Sequence< sal_Int8 >& aKey, sal_Int32 nEncAlg, sal_Int32 nCheckAlg, sal_Int32 nDerivedKeySize, sal_Int32 nStartKeyGenID, bool const bTryWrongSHA1) : BaseEncryptionData( aData ) , m_aKey( aKey ) , m_nEncAlg( nEncAlg ) , m_nCheckAlg( nCheckAlg ) , m_nDerivedKeySize( nDerivedKeySize ) , m_nStartKeyGenID( nStartKeyGenID ) + , m_bTryWrongSHA1(bTryWrongSHA1) {} EncryptionData( const EncryptionData& aData ) @@ -67,6 +69,7 @@ public: , m_nCheckAlg( aData.m_nCheckAlg ) , m_nDerivedKeySize( aData.m_nDerivedKeySize ) , m_nStartKeyGenID( aData.m_nStartKeyGenID ) + , m_bTryWrongSHA1(aData.m_bTryWrongSHA1) {} }; diff --git a/package/inc/ZipPackageStream.hxx b/package/inc/ZipPackageStream.hxx index 4620b5da64f2..67428c086948 100644 --- a/package/inc/ZipPackageStream.hxx +++ b/package/inc/ZipPackageStream.hxx @@ -83,9 +83,10 @@ public: bool IsFromManifest() const { return m_bFromManifest; } void SetFromManifest( bool bValue ) { m_bFromManifest = bValue; } - ::rtl::Reference< EncryptionData > GetEncryptionData( bool bWinEncoding = false ); + enum class Bugs { None, WinEncodingWrongSHA1, WrongSHA1 }; + ::rtl::Reference<EncryptionData> GetEncryptionData(Bugs bugs = Bugs::WrongSHA1); - css::uno::Sequence< sal_Int8 > GetEncryptionKey( bool bWinEncoding = false ); + css::uno::Sequence<sal_Int8> GetEncryptionKey(Bugs bugs = Bugs::WrongSHA1); sal_Int32 GetStartKeyGenID(); diff --git a/package/source/zipapi/ZipFile.cxx b/package/source/zipapi/ZipFile.cxx index de4c1a364acd..3d4abb65a8f9 100644 --- a/package/source/zipapi/ZipFile.cxx +++ b/package/source/zipapi/ZipFile.cxx @@ -144,7 +144,16 @@ uno::Reference< xml::crypto::XDigestContext > ZipFile::StaticGetDigestContextFor xDigestContext.set( xDigestContextSupplier->getDigestContext( xEncryptionData->m_nCheckAlg, uno::Sequence< beans::NamedValue >() ), uno::UNO_SET_THROW ); } else if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA1_1K ) - xDigestContext.set( SHA1DigestContext::Create(), uno::UNO_SET_THROW ); + { + if (xEncryptionData->m_bTryWrongSHA1) + { + xDigestContext.set(StarOfficeSHA1DigestContext::Create(), uno::UNO_SET_THROW); + } + else + { + xDigestContext.set(CorrectSHA1DigestContext::Create(), uno::UNO_SET_THROW); + } + } return xDigestContext; } diff --git a/package/source/zipapi/sha1context.cxx b/package/source/zipapi/sha1context.cxx index f24064616edb..af3123e2dbd0 100644 --- a/package/source/zipapi/sha1context.cxx +++ b/package/source/zipapi/sha1context.cxx @@ -19,6 +19,7 @@ #include <sal/config.h> +#include <comphelper/hash.hxx> #include <com/sun/star/lang/DisposedException.hpp> #include <rtl/digest.h> #include <rtl/ref.hxx> @@ -28,9 +29,9 @@ using namespace ::com::sun::star; // static -uno::Reference< xml::crypto::XDigestContext > SHA1DigestContext::Create() +uno::Reference<xml::crypto::XDigestContext> StarOfficeSHA1DigestContext::Create() { - ::rtl::Reference< SHA1DigestContext > xResult = new SHA1DigestContext(); + ::rtl::Reference<StarOfficeSHA1DigestContext> xResult = new StarOfficeSHA1DigestContext(); xResult->m_pDigest = rtl_digest_createSHA1(); if ( !xResult->m_pDigest ) throw uno::RuntimeException("Can not create cipher!" ); @@ -38,7 +39,7 @@ uno::Reference< xml::crypto::XDigestContext > SHA1DigestContext::Create() return uno::Reference< xml::crypto::XDigestContext >( xResult.get() ); } -SHA1DigestContext::~SHA1DigestContext() +StarOfficeSHA1DigestContext::~StarOfficeSHA1DigestContext() { if ( m_pDigest ) { @@ -47,7 +48,7 @@ SHA1DigestContext::~SHA1DigestContext() } } -void SAL_CALL SHA1DigestContext::updateDigest( const uno::Sequence< ::sal_Int8 >& aData ) +void SAL_CALL StarOfficeSHA1DigestContext::updateDigest(const uno::Sequence<::sal_Int8>& aData) { ::osl::MutexGuard aGuard( m_aMutex ); if ( !m_pDigest ) @@ -62,7 +63,7 @@ void SAL_CALL SHA1DigestContext::updateDigest( const uno::Sequence< ::sal_Int8 > } } -uno::Sequence< ::sal_Int8 > SAL_CALL SHA1DigestContext::finalizeDigestAndDispose() +uno::Sequence<::sal_Int8> SAL_CALL StarOfficeSHA1DigestContext::finalizeDigestAndDispose() { ::osl::MutexGuard aGuard( m_aMutex ); if ( !m_pDigest ) @@ -83,4 +84,45 @@ uno::Sequence< ::sal_Int8 > SAL_CALL SHA1DigestContext::finalizeDigestAndDispose return aResult; } +uno::Reference<xml::crypto::XDigestContext> CorrectSHA1DigestContext::Create() +{ + return new CorrectSHA1DigestContext(); +} + +struct CorrectSHA1DigestContext::Impl +{ + ::osl::Mutex m_Mutex; + ::comphelper::Hash m_Hash{::comphelper::HashType::SHA1}; + bool m_bDisposed{false}; +}; + +CorrectSHA1DigestContext::CorrectSHA1DigestContext() + : m_pImpl(new Impl) +{ +} + +CorrectSHA1DigestContext::~CorrectSHA1DigestContext() +{ +} + +void SAL_CALL CorrectSHA1DigestContext::updateDigest(const uno::Sequence<::sal_Int8>& rData) +{ + ::osl::MutexGuard aGuard(m_pImpl->m_Mutex); + if (m_pImpl->m_bDisposed) + throw lang::DisposedException(); + + m_pImpl->m_Hash.update(reinterpret_cast<unsigned char const*>(rData.getConstArray()), rData.getLength()); +} + +uno::Sequence<::sal_Int8> SAL_CALL CorrectSHA1DigestContext::finalizeDigestAndDispose() +{ + ::osl::MutexGuard aGuard(m_pImpl->m_Mutex); + if (m_pImpl->m_bDisposed) + throw lang::DisposedException(); + + m_pImpl->m_bDisposed = true; + std::vector<unsigned char> const sha1(m_pImpl->m_Hash.finalize()); + return uno::Sequence<sal_Int8>(reinterpret_cast<sal_Int8 const*>(sha1.data()), sha1.size()); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/zipapi/sha1context.hxx b/package/source/zipapi/sha1context.hxx index ef9c433082d3..436dfcccbf7c 100644 --- a/package/source/zipapi/sha1context.hxx +++ b/package/source/zipapi/sha1context.hxx @@ -24,18 +24,19 @@ #include <cppuhelper/implbase.hxx> #include <osl/mutex.hxx> -class SHA1DigestContext : public cppu::WeakImplHelper< css::xml::crypto::XDigestContext > +class StarOfficeSHA1DigestContext + : public cppu::WeakImplHelper<css::xml::crypto::XDigestContext> { ::osl::Mutex m_aMutex; void* m_pDigest; - SHA1DigestContext() + StarOfficeSHA1DigestContext() : m_pDigest( nullptr ) {} public: - virtual ~SHA1DigestContext() override; + virtual ~StarOfficeSHA1DigestContext() override; static css::uno::Reference< css::xml::crypto::XDigestContext > Create(); @@ -44,6 +45,25 @@ public: }; +class CorrectSHA1DigestContext + : public cppu::WeakImplHelper<css::xml::crypto::XDigestContext> +{ + struct Impl; + std::unique_ptr<Impl> m_pImpl; + + CorrectSHA1DigestContext(); + +public: + + virtual ~CorrectSHA1DigestContext() override; + + static css::uno::Reference<css::xml::crypto::XDigestContext> Create(); + + virtual void SAL_CALL updateDigest(const css::uno::Sequence<::sal_Int8>& rData) override; + virtual css::uno::Sequence<::sal_Int8> SAL_CALL finalizeDigestAndDispose() override; + +}; + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/zippackage/ZipPackageStream.cxx b/package/source/zippackage/ZipPackageStream.cxx index bd914b663406..c9e987aec1bd 100644 --- a/package/source/zippackage/ZipPackageStream.cxx +++ b/package/source/zippackage/ZipPackageStream.cxx @@ -198,26 +198,27 @@ sal_Int32 ZipPackageStream::GetBlockSize() const return GetEncryptionAlgorithm() == css::xml::crypto::CipherID::AES_CBC_W3C_PADDING ? 16 : 8; } -::rtl::Reference< EncryptionData > ZipPackageStream::GetEncryptionData( bool bUseWinEncoding ) +::rtl::Reference<EncryptionData> ZipPackageStream::GetEncryptionData(Bugs const bugs) { ::rtl::Reference< EncryptionData > xResult; if ( m_xBaseEncryptionData.is() ) xResult = new EncryptionData( *m_xBaseEncryptionData, - GetEncryptionKey( bUseWinEncoding ), + GetEncryptionKey(bugs), GetEncryptionAlgorithm(), m_nImportedChecksumAlgorithm ? m_nImportedChecksumAlgorithm : m_rZipPackage.GetChecksumAlgID(), m_nImportedDerivedKeySize ? m_nImportedDerivedKeySize : m_rZipPackage.GetDefaultDerivedKeySize(), - GetStartKeyGenID() ); + GetStartKeyGenID(), + bugs != Bugs::None); return xResult; } -uno::Sequence< sal_Int8 > ZipPackageStream::GetEncryptionKey( bool bUseWinEncoding ) +uno::Sequence<sal_Int8> ZipPackageStream::GetEncryptionKey(Bugs const bugs) { uno::Sequence< sal_Int8 > aResult; sal_Int32 nKeyGenID = GetStartKeyGenID(); - bUseWinEncoding = ( bUseWinEncoding || m_bUseWinEncoding ); + bool const bUseWinEncoding = (bugs == Bugs::WinEncodingWrongSHA1 || m_bUseWinEncoding); if ( m_bHaveOwnKey && m_aStorageEncryptionKeys.getLength() ) { @@ -226,7 +227,11 @@ uno::Sequence< sal_Int8 > ZipPackageStream::GetEncryptionKey( bool bUseWinEncodi aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA256UTF8; else if ( nKeyGenID == xml::crypto::DigestID::SHA1 ) { - aNameToFind = bUseWinEncoding ? OUString(PACKAGE_ENCRYPTIONDATA_SHA1MS1252) : OUString(PACKAGE_ENCRYPTIONDATA_SHA1UTF8); + aNameToFind = bUseWinEncoding + ? OUString(PACKAGE_ENCRYPTIONDATA_SHA1MS1252) + : (bugs == Bugs::WrongSHA1) + ? OUString(PACKAGE_ENCRYPTIONDATA_SHA1UTF8) + : OUString(PACKAGE_ENCRYPTIONDATA_SHA1CORRECT); } else throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" ); @@ -1007,12 +1012,23 @@ uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getDataStream() uno::Reference< io::XInputStream > xResult; try { - xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() ); + xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::WrongSHA1), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() ); } catch( const packages::WrongPasswordException& ) { if ( m_rZipPackage.GetStartKeyGenID() == xml::crypto::DigestID::SHA1 ) { + SAL_WARN("package", "ZipPackageStream::getDataStream(): SHA1 mismatch, trying fallbacks..."); + try + { // tdf#114939 try without legacy StarOffice SHA1 bug + xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::None), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() ); + return xResult; + } + catch (const packages::WrongPasswordException&) + { + /* ignore and try next... */ + } + try { // rhbz#1013844 / fdo#47482 workaround for the encrypted @@ -1035,7 +1051,7 @@ uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getDataStream() // workaround for the encrypted documents generated with the old OOo1.x bug. if ( !m_bUseWinEncoding ) { - xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData( true ), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() ); + xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::WinEncodingWrongSHA1), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() ); m_bUseWinEncoding = true; } else diff --git a/sw/qa/extras/inc/swmodeltestbase.hxx b/sw/qa/extras/inc/swmodeltestbase.hxx index 7cf82ad81311..6a0d6a1ff316 100644 --- a/sw/qa/extras/inc/swmodeltestbase.hxx +++ b/sw/qa/extras/inc/swmodeltestbase.hxx @@ -688,11 +688,18 @@ protected: aMediaDescriptor["FilterOptions"] <<= maFilterOptions; if (pPassword) { - OUString sPassword = OUString::createFromAscii(pPassword); - css::uno::Sequence<css::beans::NamedValue> aEncryptionData { - { "OOXPassword", css::uno::makeAny(sPassword) } - }; - aMediaDescriptor[utl::MediaDescriptor::PROP_ENCRYPTIONDATA()] <<= aEncryptionData; + if (strcmp(pFilter, "Office Open XML Text")) + { + aMediaDescriptor["Password"] <<= OUString::createFromAscii(pPassword); + } + else + { + OUString sPassword = OUString::createFromAscii(pPassword); + css::uno::Sequence<css::beans::NamedValue> aEncryptionData { + { "OOXPassword", css::uno::makeAny(sPassword) } + }; + aMediaDescriptor[utl::MediaDescriptor::PROP_ENCRYPTIONDATA()] <<= aEncryptionData; + } } xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); uno::Reference<lang::XComponent> xComponent(xStorable, uno::UNO_QUERY); diff --git a/sw/qa/extras/odfexport/data/sha1_correct.odt b/sw/qa/extras/odfexport/data/sha1_correct.odt new file mode 100644 index 000000000000..01cbb0a073b2 Binary files /dev/null and b/sw/qa/extras/odfexport/data/sha1_correct.odt differ diff --git a/sw/qa/extras/odfexport/data/sha1_wrong.odt b/sw/qa/extras/odfexport/data/sha1_wrong.odt new file mode 100644 index 000000000000..94032025b3d9 Binary files /dev/null and b/sw/qa/extras/odfexport/data/sha1_wrong.odt differ diff --git a/sw/qa/extras/odfexport/odfexport.cxx b/sw/qa/extras/odfexport/odfexport.cxx index 4e57a415e786..d4f8991b56de 100644 --- a/sw/qa/extras/odfexport/odfexport.cxx +++ b/sw/qa/extras/odfexport/odfexport.cxx @@ -384,6 +384,16 @@ DECLARE_ODFEXPORT_TEST(testFramebackgrounds, "framebackgrounds.odt") } } +DECLARE_SW_ROUNDTRIP_TEST(testSHA1Correct, "sha1_correct.odt", "1012345678901234567890123456789012345678901234567890", Test) +{ // tdf#114939 this has both an affected password as well as content.xml + getParagraph(1, "012"); +} + +DECLARE_SW_ROUNDTRIP_TEST(testSHA1Wrong, "sha1_wrong.odt", "1012345678901234567890123456789012345678901234567890", Test) +{ // tdf#114939 this has both an affected password as well as content.xml + getParagraph(1, "012"); +} + DECLARE_ODFEXPORT_TEST(testOOoxmlEmbedded, "oooxml_embedded.sxw") { uno::Reference<text::XTextEmbeddedObjectsSupplier> xTEOSupplier(mxComponent, uno::UNO_QUERY); commit 64592a19e5d512fb5cd09bf0a1726b9c78481e65 Author: Michael Stahl <[email protected]> Date: Fri Jan 12 14:49:59 2018 +0100 test: cancel interaction on password *re-entry* If the password was wrong the first time, it's not going to be right if we return it a second time. Avoid infinite loops in tests. Change-Id: Ie4bf92853d0789b0114250f6c8ded1b2c3ab184d diff --git a/include/test/testinteractionhandler.hxx b/include/test/testinteractionhandler.hxx index df4316c18ef0..805d72ba0d0b 100644 --- a/include/test/testinteractionhandler.hxx +++ b/include/test/testinteractionhandler.hxx @@ -100,6 +100,7 @@ public: { bool bPasswordRequestFound = false; bool bIsRequestPasswordToModify = false; + css::task::PasswordRequestMode mode{}; OString sUrl; @@ -108,6 +109,7 @@ public: { bIsRequestPasswordToModify = passwordRequest2.IsRequestPasswordToModify; sUrl = passwordRequest2.Name.toUtf8(); + mode = passwordRequest2.Mode; bPasswordRequestFound = true; } css::task::DocumentMSPasswordRequest2 passwordMSRequest2; @@ -115,6 +117,7 @@ public: { bIsRequestPasswordToModify = passwordMSRequest2.IsRequestPasswordToModify; sUrl = passwordMSRequest2.Name.toUtf8(); + mode = passwordMSRequest2.Mode; bPasswordRequestFound = true; } @@ -127,7 +130,13 @@ public: for (sal_Int32 i = 0; i < rContinuations.getLength(); ++i) { - if (bIsRequestPasswordToModify) + if (mode == css::task::PasswordRequestMode_PASSWORD_REENTER) + { // cancel re-enter of wrong password, to avoid infinite loop + css::uno::Reference<css::task::XInteractionAbort> const xAbort(rContinuations[i], css::uno::UNO_QUERY); + if (xAbort.is()) + xAbort->select(); + } + else if (bIsRequestPasswordToModify) { css::uno::Reference<css::task::XInteractionPassword2> const xIPW2(rContinuations[i], css::uno::UNO_QUERY); xIPW2->setPasswordToModify(msPassword); commit 0b7c3b7d9fa71f59eed75c3e80e5e12245c5e1c5 Author: Michael Stahl <[email protected]> Date: Thu Jan 11 15:55:25 2018 +0100 tdf#114939 officecfg,sfx2: always use AES/SHA256 in ODF 1.2 The setting ODFVER_012_EXT_COMPAT "1.2 Extended (compatibility mode)" no longer has an effect on the encryption algorithms. API CHANGE: Remove the configuration settings: Office.Common.Save.ODF.UseSHA1InODF12 Office.Common.Save.ODF.UseBlowfishInODF12 Rationale: * Every release since 2012, from LO >= 3.4 and AOO >= 3.4, can read AES/SHA256 encrypted files. * SHA1 is broken anyway * tdf#114939 causes SHA1 interop issues Retain SHA1/Blowfish only for ODF 1.1 export. Change-Id: I007511d4830a90121e38bf5bb4534df5695621e9 diff --git a/officecfg/registry/schema/org/openoffice/Office/Common.xcs b/officecfg/registry/schema/org/openoffice/Office/Common.xcs index 2884179d0800..2901733cabc0 100644 --- a/officecfg/registry/schema/org/openoffice/Office/Common.xcs +++ b/officecfg/registry/schema/org/openoffice/Office/Common.xcs @@ -2409,21 +2409,6 @@ </constraints> <value>3</value> </prop> - <prop oor:name="UseSHA1InODF12" oor:type="xs:boolean" oor:nillable="false"> - <info> - <desc>Specifies whether SHA1 algorithm instead of SHA256 should be - used in ODF12 for StartKey and Checksum generation during - encryption.</desc> - </info> - <value>false</value> - </prop> - <prop oor:name="UseBlowfishInODF12" oor:type="xs:boolean" oor:nillable="false"> - <info> - <desc>Specifies whether Blowfish algorithm instead of AES should be - used in ODF12 for encryption.</desc> - </info> - <value>false</value> - </prop> </group> </group> <group oor:name="Load"> diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx index 44f6f1556f75..3bac7ae20b97 100644 --- a/sfx2/source/doc/objstor.cxx +++ b/sfx2/source/doc/objstor.cxx @@ -362,15 +362,10 @@ void SfxObjectShell::SetupStorage( const uno::Reference< embed::XStorage >& xSto } SvtSaveOptions::ODFDefaultVersion nDefVersion = SvtSaveOptions::ODFVER_012; - bool bUseSHA1InODF12 = false; - bool bUseBlowfishInODF12 = false; - if (!utl::ConfigManager::IsFuzzing()) { SvtSaveOptions aSaveOpt; nDefVersion = aSaveOpt.GetODFDefaultVersion(); - bUseSHA1InODF12 = officecfg::Office::Common::Save::ODF::UseSHA1InODF12::get(); - bUseBlowfishInODF12 = officecfg::Office::Common::Save::ODF::UseBlowfishInODF12::get(); } // the default values, that should be used for ODF1.1 and older formats @@ -392,13 +387,9 @@ void SfxObjectShell::SetupStorage( const uno::Reference< embed::XStorage >& xSto { } - if ( !bUseSHA1InODF12 && nDefVersion != SvtSaveOptions::ODFVER_012_EXT_COMPAT ) - { - aEncryptionAlgs[0].Value <<= xml::crypto::DigestID::SHA256; - aEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K; - } - if ( !bUseBlowfishInODF12 && nDefVersion != SvtSaveOptions::ODFVER_012_EXT_COMPAT ) - aEncryptionAlgs[1].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING; + aEncryptionAlgs[0].Value <<= xml::crypto::DigestID::SHA256; + aEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K; + aEncryptionAlgs[1].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING; } try _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
