comphelper/source/misc/docpasswordhelper.cxx | 4 + comphelper/source/misc/hash.cxx | 6 ++ include/comphelper/hash.hxx | 1 include/filter/msfilter/mscodec.hxx | 1 include/oox/crypto/AgileEngine.hxx | 1 include/oox/crypto/CryptTools.hxx | 1 oox/qa/unit/CryptoTest.cxx | 62 +++++++++++++++++++++++++++ oox/source/crypto/AgileEngine.cxx | 33 +++++++++++++- oox/source/crypto/CryptTools.cxx | 6 ++ 9 files changed, 112 insertions(+), 3 deletions(-)
New commits: commit 4fff1101da5d953c76da6f0f247e24935fb28ca6 Author: Balazs Varga <[email protected]> AuthorDate: Tue Aug 22 22:10:20 2023 +0200 Commit: Balazs Varga <[email protected]> CommitDate: Thu Aug 31 11:47:30 2023 +0200 tdf#156835 - FILEOPEN XLSX: add SHA-384 encryption support for ooxml import Password protected file with SHA-384 encryption does not open before this patch. cherry-picked from: 9254fbce6b9e20a75aa2a379bcf2fc9dc41a5b44 Change-Id: I482233f788b8e9da210ad6d2a6c4ece18d05d248 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/156282 Tested-by: Jenkins Reviewed-by: Samuel Mehrbrodt <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/156309 Tested-by: Balazs Varga <[email protected]> Reviewed-by: Balazs Varga <[email protected]> diff --git a/comphelper/source/misc/docpasswordhelper.cxx b/comphelper/source/misc/docpasswordhelper.cxx index dfc408690027..946737994ab7 100644 --- a/comphelper/source/misc/docpasswordhelper.cxx +++ b/comphelper/source/misc/docpasswordhelper.cxx @@ -268,7 +268,9 @@ std::vector<unsigned char> DocPasswordHelper::GetOoxHashAsVector( eType = comphelper::HashType::SHA512; else if (rAlgorithmName == "SHA-256" || rAlgorithmName == "SHA256") eType = comphelper::HashType::SHA256; - else if (rAlgorithmName == "SHA-1" || rAlgorithmName == "SHA1") // "SHA1" might be in the wild + else if (rAlgorithmName == u"SHA-384" || rAlgorithmName == u"SHA384") + eType = comphelper::HashType::SHA384; + else if (rAlgorithmName == u"SHA-1" || rAlgorithmName == u"SHA1") // "SHA1" might be in the wild eType = comphelper::HashType::SHA1; else if (rAlgorithmName == "MD5") eType = comphelper::HashType::MD5; diff --git a/comphelper/source/misc/hash.cxx b/comphelper/source/misc/hash.cxx index 3db0b3e56616..ca5467904c22 100644 --- a/comphelper/source/misc/hash.cxx +++ b/comphelper/source/misc/hash.cxx @@ -39,6 +39,8 @@ struct HashImpl return HASH_AlgSHA1; case HashType::SHA256: return HASH_AlgSHA256; + case HashType::SHA384: + return HASH_AlgSHA384; case HashType::SHA512: return HASH_AlgSHA512; } @@ -58,6 +60,8 @@ struct HashImpl return EVP_sha1(); case HashType::SHA256: return EVP_sha256(); + case HashType::SHA384: + return EVP_sha384(); case HashType::SHA512: return EVP_sha512(); } @@ -138,6 +142,8 @@ size_t Hash::getLength() const return 20; case HashType::SHA256: return 32; + case HashType::SHA384: + return 48; case HashType::SHA512: return 64; } diff --git a/include/comphelper/hash.hxx b/include/comphelper/hash.hxx index 52ad5e5cdf1e..ae16d5ddd57f 100644 --- a/include/comphelper/hash.hxx +++ b/include/comphelper/hash.hxx @@ -26,6 +26,7 @@ enum class HashType MD5, SHA1, SHA256, + SHA384, SHA512 }; diff --git a/include/filter/msfilter/mscodec.hxx b/include/filter/msfilter/mscodec.hxx index 47067a38fe5e..9f0a858fb227 100644 --- a/include/filter/msfilter/mscodec.hxx +++ b/include/filter/msfilter/mscodec.hxx @@ -444,6 +444,7 @@ const sal_uInt32 SALT_LENGTH = 16; const sal_uInt32 ENCRYPTED_VERIFIER_LENGTH = 16; const sal_uInt32 SHA1_HASH_LENGTH = RTL_DIGEST_LENGTH_SHA1; // 20 const sal_uInt32 SHA256_HASH_LENGTH = 32; +const sal_uInt32 SHA384_HASH_LENGTH = 48; const sal_uInt32 SHA512_HASH_LENGTH = 64; struct MSFILTER_DLLPUBLIC EncryptionStandardHeader diff --git a/include/oox/crypto/AgileEngine.hxx b/include/oox/crypto/AgileEngine.hxx index 796f030b3bce..0a1721dc096b 100644 --- a/include/oox/crypto/AgileEngine.hxx +++ b/include/oox/crypto/AgileEngine.hxx @@ -71,6 +71,7 @@ struct OOX_DLLPUBLIC AgileEncryptionParameters enum class AgileEncryptionPreset { AES_128_SHA1, + AES_128_SHA384, AES_256_SHA512, }; diff --git a/include/oox/crypto/CryptTools.hxx b/include/oox/crypto/CryptTools.hxx index 5bd7119c548c..0ff8f1c28bed 100644 --- a/include/oox/crypto/CryptTools.hxx +++ b/include/oox/crypto/CryptTools.hxx @@ -49,6 +49,7 @@ enum class CryptoHashType { SHA1, SHA256, + SHA384, SHA512 }; diff --git a/oox/qa/unit/CryptoTest.cxx b/oox/qa/unit/CryptoTest.cxx index 47d567fab34b..38aad4dff4b8 100644 --- a/oox/qa/unit/CryptoTest.cxx +++ b/oox/qa/unit/CryptoTest.cxx @@ -82,6 +82,15 @@ void CryptoTest::testCryptoHash() toString(aHash)); } + { + oox::core::CryptoHash aCryptoHash(aKey, oox::core::CryptoHashType::SHA384); + aCryptoHash.update(aContent); + std::vector<sal_uInt8> aHash = aCryptoHash.finalize(); + CPPUNIT_ASSERT_EQUAL(std::string("d7f4727e2c0b39ae0f1e40cc96f60242d5b7801841cea6fc592c5d3e1" + "ae50700582a96cf35e1e554995fe4e03381c237"), + toString(aHash)); + } + { oox::core::CryptoHash aCryptoHash(aKey, oox::core::CryptoHashType::SHA512); aCryptoHash.update(aContent); @@ -184,6 +193,13 @@ void CryptoTest::testAgileEncryptionVerifier() CPPUNIT_ASSERT_EQUAL(false, aEngine.decryptAndCheckVerifierHash("Wrong")); CPPUNIT_ASSERT_EQUAL(true, aEngine.decryptAndCheckVerifierHash(aPassword)); + aEngine.setupEncryptionParameters({ 100000, 16, 128, 48, 16, OUString("AES"), + OUString("ChainingModeCBC"), OUString("SHA384") }); + + CPPUNIT_ASSERT_EQUAL(true, aEngine.generateAndEncryptVerifierHash(aPassword)); + CPPUNIT_ASSERT_EQUAL(false, aEngine.decryptAndCheckVerifierHash("Wrong")); + CPPUNIT_ASSERT_EQUAL(true, aEngine.decryptAndCheckVerifierHash(aPassword)); + aEngine.setupEncryptionParameters({ 100000, 16, 256, 64, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA512") }); @@ -243,6 +259,52 @@ void CryptoTest::testAgileEncrpytionInfoWritingAndParsing() } } + { // Preset AES128 - SHA384 + SvMemoryStream aEncryptionInfo; + { + oox::core::AgileEngine aEngine; + + aEngine.setPreset(oox::core::AgileEncryptionPreset::AES_128_SHA384); + aEngine.setupEncryption(aPassword); + aKeyDataSalt = aEngine.getInfo().keyDataSalt; + + oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream( + new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), true); + + aEngine.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream); + aBinaryEncryptionInfoOutputStream.close(); + + CPPUNIT_ASSERT_EQUAL(sal_uInt64(1040), aEncryptionInfo.GetSize()); + } + + aEncryptionInfo.Seek(STREAM_SEEK_TO_BEGIN); + + { + oox::core::AgileEngine aEngine; + + uno::Reference<io::XInputStream> xInputStream( + new utl::OSeekableInputStreamWrapper(aEncryptionInfo)); + + xInputStream->skipBytes(4); // Encryption type -> Agile + + CPPUNIT_ASSERT(aEngine.readEncryptionInfo(xInputStream)); + + oox::core::AgileEncryptionInfo& rInfo = aEngine.getInfo(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(100000), rInfo.spinCount); + CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo.saltSize); + CPPUNIT_ASSERT_EQUAL(sal_Int32(128), rInfo.keyBits); + CPPUNIT_ASSERT_EQUAL(sal_Int32(48), rInfo.hashSize); + CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo.blockSize); + CPPUNIT_ASSERT_EQUAL(OUString("AES"), rInfo.cipherAlgorithm); + CPPUNIT_ASSERT_EQUAL(OUString("ChainingModeCBC"), rInfo.cipherChaining); + CPPUNIT_ASSERT_EQUAL(OUString("SHA384"), rInfo.hashAlgorithm); + CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt), toString(rInfo.keyDataSalt)); + + CPPUNIT_ASSERT_EQUAL(false, aEngine.decryptAndCheckVerifierHash("Wrong")); + CPPUNIT_ASSERT_EQUAL(true, aEngine.decryptAndCheckVerifierHash(aPassword)); + } + } + { // Preset AES256 - SHA512 SvMemoryStream aEncryptionInfo; { diff --git a/oox/source/crypto/AgileEngine.cxx b/oox/source/crypto/AgileEngine.cxx index 7c2a0e9c93d2..81c03663cb37 100644 --- a/oox/source/crypto/AgileEngine.cxx +++ b/oox/source/crypto/AgileEngine.cxx @@ -201,7 +201,13 @@ bool hashCalc(std::vector<sal_uInt8>& output, output = out; return true; } - else if (sAlgorithm == "SHA512") + else if (sAlgorithm == u"SHA384") + { + std::vector<unsigned char> out = comphelper::Hash::calculateHash(input.data(), input.size(), comphelper::HashType::SHA384); + output = out; + return true; + } + else if (sAlgorithm == u"SHA512") { std::vector<unsigned char> out = comphelper::Hash::calculateHash(input.data(), input.size(), comphelper::HashType::SHA512); output = out; @@ -214,7 +220,10 @@ CryptoHashType cryptoHashTypeFromString(OUString const & sAlgorithm) { if (sAlgorithm == "SHA512") return CryptoHashType::SHA512; - return CryptoHashType::SHA1; + else if (sAlgorithm == u"SHA384") + return CryptoHashType::SHA384; + else + return CryptoHashType::SHA1; } } // namespace @@ -370,6 +379,8 @@ bool AgileEngine::decryptHmacKey() comphelper::HashType eType; if (mInfo.hashAlgorithm == "SHA1") eType = comphelper::HashType::SHA1; + else if (mInfo.hashAlgorithm == "SHA384") + eType = comphelper::HashType::SHA384; else if (mInfo.hashAlgorithm == "SHA512") eType = comphelper::HashType::SHA512; else @@ -396,6 +407,8 @@ bool AgileEngine::decryptHmacValue() comphelper::HashType eType; if (mInfo.hashAlgorithm == "SHA1") eType = comphelper::HashType::SHA1; + else if (mInfo.hashAlgorithm == "SHA384") + eType = comphelper::HashType::SHA384; else if (mInfo.hashAlgorithm == "SHA512") eType = comphelper::HashType::SHA512; else @@ -534,6 +547,16 @@ bool AgileEngine::readEncryptionInfo(uno::Reference<io::XInputStream> & rxInputS return true; } + // AES 128 CBC with SHA384 + if (mInfo.keyBits == 128 && + mInfo.cipherAlgorithm == "AES" && + mInfo.cipherChaining == "ChainingModeCBC" && + mInfo.hashAlgorithm == "SHA384" && + mInfo.hashSize == msfilter::SHA384_HASH_LENGTH) + { + return true; + } + // AES 256 CBC with SHA512 if (mInfo.keyBits == 256 && mInfo.cipherAlgorithm == "AES" && @@ -597,6 +620,8 @@ bool AgileEngine::encryptHmacKey() comphelper::HashType eType; if (mInfo.hashAlgorithm == "SHA1") eType = comphelper::HashType::SHA1; + else if (mInfo.hashAlgorithm == "SHA384") + eType = comphelper::HashType::SHA384; else if (mInfo.hashAlgorithm == "SHA512") eType = comphelper::HashType::SHA512; else @@ -624,6 +649,8 @@ bool AgileEngine::encryptHmacValue() comphelper::HashType eType; if (mInfo.hashAlgorithm == "SHA1") eType = comphelper::HashType::SHA1; + else if (mInfo.hashAlgorithm == "SHA384") + eType = comphelper::HashType::SHA384; else if (mInfo.hashAlgorithm == "SHA512") eType = comphelper::HashType::SHA512; else @@ -663,6 +690,8 @@ bool AgileEngine::setupEncryption(OUString const & rPassword) { if (meEncryptionPreset == AgileEncryptionPreset::AES_128_SHA1) setupEncryptionParameters({ 100000, 16, 128, 20, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA1") }); + else if (meEncryptionPreset == AgileEncryptionPreset::AES_128_SHA384) + setupEncryptionParameters({ 100000, 16, 128, 48, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA384") }); else setupEncryptionParameters({ 100000, 16, 256, 64, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA512") }); diff --git a/oox/source/crypto/CryptTools.cxx b/oox/source/crypto/CryptTools.cxx index 6ca316b0a9b6..8c70c3eaf585 100644 --- a/oox/source/crypto/CryptTools.cxx +++ b/oox/source/crypto/CryptTools.cxx @@ -90,6 +90,8 @@ struct CryptoImpl aEvpMd = EVP_sha1(); break; case CryptoHashType::SHA256: aEvpMd = EVP_sha256(); break; + case CryptoHashType::SHA384: + aEvpMd = EVP_sha384(); break; case CryptoHashType::SHA512: aEvpMd = EVP_sha512(); break; } @@ -284,6 +286,9 @@ struct CryptoImpl case CryptoHashType::SHA256: aMechanism = CKM_SHA256_HMAC; break; + case CryptoHashType::SHA384: + aMechanism = CKM_SHA384_HMAC; + break; case CryptoHashType::SHA512: aMechanism = CKM_SHA512_HMAC; break; @@ -428,6 +433,7 @@ sal_Int32 getSizeForHashType(CryptoHashType eType) { case CryptoHashType::SHA1: return 20; case CryptoHashType::SHA256: return 32; + case CryptoHashType::SHA384: return 48; case CryptoHashType::SHA512: return 64; } return 0;
