comphelper/qa/unit/CryptoTest.cxx | 98 ++++++++++++++++++++++++++++ comphelper/source/crypto/Crypto_NSS.cxx | 5 - comphelper/source/crypto/Crypto_OpenSSL.cxx | 4 + include/comphelper/crypto/Crypto.hxx | 2 include/oox/crypto/AgileEngine.hxx | 1 oox/source/crypto/AgileEngine.cxx | 24 +++++- 6 files changed, 127 insertions(+), 7 deletions(-)
New commits: commit a399c4fae0a3f7dfd00565929e7ad6a41bde0df8 Author: Julien Nabet <serval2...@yahoo.fr> AuthorDate: Mon Apr 21 15:17:06 2025 +0200 Commit: Julien Nabet <serval2...@yahoo.fr> CommitDate: Wed Apr 23 17:22:07 2025 +0200 tdf#166241: add AES_192_CBC/AES_192_EBC + improve SALT management by taking the max between encryptedHashInput.size() and comphelper::roundUp(mInfo.saltSize, mInfo.blockSize) + add QA tests Change-Id: I90dce73928e0b204500ac659eb80cd1499a4f065 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184407 Tested-by: Jenkins Reviewed-by: Julien Nabet <serval2...@yahoo.fr> Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/comphelper/qa/unit/CryptoTest.cxx b/comphelper/qa/unit/CryptoTest.cxx index 4c7603d88eb6..95b7a1b11781 100644 --- a/comphelper/qa/unit/CryptoTest.cxx +++ b/comphelper/qa/unit/CryptoTest.cxx @@ -185,4 +185,102 @@ CPPUNIT_TEST_FIXTURE(CryptoTest, testEncrypt_AES256_ECB) } } +CPPUNIT_TEST_FIXTURE(CryptoTest, testEncrypt_AES192_CBC) +{ + std::vector<sal_uInt8> key = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32 }; + + std::vector<sal_uInt8> iv = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; + + std::vector<sal_uInt8> original = { 's', 'e', 'c', 'r', 'e', 't', ' + + std::vector<sal_uInt8> encrypted(original.size()); + + { + sal_uInt32 nWrittenSize = 0; + comphelper::Encrypt aEncryptor(key, iv, comphelper::CryptoType::AES_192_CBC); + nWrittenSize = aEncryptor.update(encrypted, original); + + // nothing should be written as the size of the input is not a multiple of block size + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), nWrittenSize); + } + + { + sal_uInt32 nWrittenSize = 0; + comphelper::Encrypt aEncryptor(key, iv, comphelper::CryptoType::AES_192_CBC); + + original.resize(16, 0); // apply padding to make it multiple of block size + encrypted.resize(16, 0); + + CPPUNIT_ASSERT_EQUAL(std::string("73656372657400000000000000000000"), + comphelper::hashToString(original)); + + nWrittenSize = aEncryptor.update(encrypted, original); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(16), nWrittenSize); + + CPPUNIT_ASSERT_EQUAL(std::string("e75cb91a34377c09c354c24fcef345a6"), + comphelper::hashToString(encrypted)); + + std::vector<sal_uInt8> decrypted(encrypted.size(), 0); + + comphelper::Decrypt aDecryptor(key, iv, comphelper::CryptoType::AES_192_CBC); + nWrittenSize = aDecryptor.update(decrypted, encrypted); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(16), nWrittenSize); + + CPPUNIT_ASSERT_EQUAL(std::string("73656372657400000000000000000000"), + comphelper::hashToString(decrypted)); + } +} + +CPPUNIT_TEST_FIXTURE(CryptoTest, testEncrypt_AES192_ECB) +{ + std::vector<sal_uInt8> key = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32 }; + + std::vector<sal_uInt8> iv = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; + + std::vector<sal_uInt8> original = { 's', 'e', 'c', 'r', 'e', 't', ' + + std::vector<sal_uInt8> encrypted(original.size()); + + { + sal_uInt32 nWrittenSize = 0; + comphelper::Encrypt aEncryptor(key, iv, comphelper::CryptoType::AES_192_ECB); + nWrittenSize = aEncryptor.update(encrypted, original); + + // nothing should be written as the size of the input is not a multiple of block size + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), nWrittenSize); + } + + { + sal_uInt32 nWrittenSize = 0; + comphelper::Encrypt aEncryptor(key, iv, comphelper::CryptoType::AES_192_ECB); + + original.resize(16, 0); // apply padding to make it multiple of block size + encrypted.resize(16, 0); + + CPPUNIT_ASSERT_EQUAL(std::string("73656372657400000000000000000000"), + comphelper::hashToString(original)); + + nWrittenSize = aEncryptor.update(encrypted, original); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(16), nWrittenSize); + + CPPUNIT_ASSERT_EQUAL(std::string("abf7abec9a6b58c089e902397c47ac49"), + comphelper::hashToString(encrypted)); + + std::vector<sal_uInt8> decrypted(encrypted.size(), 0); + + comphelper::Decrypt aDecryptor(key, iv, comphelper::CryptoType::AES_192_ECB); + nWrittenSize = aDecryptor.update(decrypted, encrypted); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(16), nWrittenSize); + + CPPUNIT_ASSERT_EQUAL(std::string("73656372657400000000000000000000"), + comphelper::hashToString(decrypted)); + } +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/crypto/Crypto_NSS.cxx b/comphelper/source/crypto/Crypto_NSS.cxx index 4f850735a42f..08c8bb85c288 100644 --- a/comphelper/source/crypto/Crypto_NSS.cxx +++ b/comphelper/source/crypto/Crypto_NSS.cxx @@ -169,13 +169,12 @@ public: switch (type) { case CryptoType::AES_128_ECB: + case CryptoType::AES_192_ECB: case CryptoType::AES_256_ECB: mechanism = CKM_AES_ECB; break; case CryptoType::AES_128_CBC: - mechanism = CKM_AES_CBC; - pIvItem = &ivItem; - break; + case CryptoType::AES_192_CBC: case CryptoType::AES_256_CBC: mechanism = CKM_AES_CBC; pIvItem = &ivItem; diff --git a/comphelper/source/crypto/Crypto_OpenSSL.cxx b/comphelper/source/crypto/Crypto_OpenSSL.cxx index 37c5ff44e222..c4501052ec17 100644 --- a/comphelper/source/crypto/Crypto_OpenSSL.cxx +++ b/comphelper/source/crypto/Crypto_OpenSSL.cxx @@ -144,10 +144,14 @@ public: { case CryptoType::AES_128_ECB: return EVP_aes_128_ecb(); + case CryptoType::AES_192_ECB: + return EVP_aes_192_ecb(); case CryptoType::AES_256_ECB: return EVP_aes_256_ecb(); case CryptoType::AES_128_CBC: return EVP_aes_128_cbc(); + case CryptoType::AES_192_CBC: + return EVP_aes_192_cbc(); case CryptoType::AES_256_CBC: return EVP_aes_256_cbc(); default: diff --git a/include/comphelper/crypto/Crypto.hxx b/include/comphelper/crypto/Crypto.hxx index abebf9440a5a..bacc2c7bb535 100644 --- a/include/comphelper/crypto/Crypto.hxx +++ b/include/comphelper/crypto/Crypto.hxx @@ -46,6 +46,8 @@ enum class CryptoType UNKNOWN, AES_128_ECB, AES_128_CBC, + AES_192_ECB, + AES_192_CBC, AES_256_ECB, AES_256_CBC, }; diff --git a/include/oox/crypto/AgileEngine.hxx b/include/oox/crypto/AgileEngine.hxx index e3daf7f971a9..787a311c4487 100644 --- a/include/oox/crypto/AgileEngine.hxx +++ b/include/oox/crypto/AgileEngine.hxx @@ -65,6 +65,7 @@ enum class AgileEncryptionPreset { AES_128_SHA1, AES_128_SHA384, + AES_192_SHA384, AES_256_SHA512, }; diff --git a/oox/source/crypto/AgileEngine.cxx b/oox/source/crypto/AgileEngine.cxx index 6b552620f24d..a74533f95849 100644 --- a/oox/source/crypto/AgileEngine.cxx +++ b/oox/source/crypto/AgileEngine.cxx @@ -8,6 +8,7 @@ * */ +#include <algorithm> #include <oox/crypto/AgileEngine.hxx> #include <oox/helper/binaryinputstream.hxx> @@ -238,6 +239,8 @@ comphelper::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo) { if (rInfo.keyBits == 128 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC") return comphelper::CryptoType::AES_128_CBC; + else if (rInfo.keyBits == 192 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC") + return comphelper::CryptoType::AES_192_CBC; else if (rInfo.keyBits == 256 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC") return comphelper::CryptoType::AES_256_CBC; return comphelper::CryptoType::UNKNOWN; @@ -337,10 +340,7 @@ bool AgileEngine::decryptAndCheckVerifierHash(OUString const & rPassword) calculateHashFinal(rPassword, hashFinal); std::vector<sal_uInt8>& encryptedHashInput = mInfo.encryptedVerifierHashInput; - // SALT - needs to be a multiple of block size (?) - sal_uInt32 nSaltSize = comphelper::roundUp(mInfo.saltSize, mInfo.blockSize); - if (nSaltSize < encryptedHashInput.size()) - return false; + sal_uInt32 nSaltSize = std::max<sal_uInt32>(comphelper::roundUp(mInfo.saltSize, mInfo.blockSize), encryptedHashInput.size()); std::vector<sal_uInt8> hashInput(nSaltSize, 0); calculateBlock(constBlock1, hashFinal, encryptedHashInput, hashInput); @@ -358,6 +358,10 @@ void AgileEngine::decryptEncryptionKey(OUString const & rPassword) sal_Int32 nKeySize = mInfo.keyBits / 8; mKey.clear(); + // tdf#166241: for AES 192 + // mKey is the outbuf and in that moment, mInfo.encryptedKeyValue length is 32, while mKey size is 24. + // so the end result is: we simply need to reserve mKey for mInfo.encryptedKeyValue.size() before resizing. + mKey.reserve(mInfo.encryptedKeyValue.size()); mKey.resize(nKeySize, 0); std::vector<sal_uInt8> aPasswordHash(mInfo.hashSize, 0); @@ -570,6 +574,16 @@ bool AgileEngine::readEncryptionInfo(uno::Reference<io::XInputStream> & rxInputS return true; } + // AES 192 CBC with SHA384 + if (mInfo.keyBits == 192 && + mInfo.cipherAlgorithm == "AES" && + mInfo.cipherChaining == "ChainingModeCBC" && + mInfo.hashAlgorithm == "SHA384" && + mInfo.hashSize == comphelper::SHA384_HASH_LENGTH) + { + return true; + } + // AES 256 CBC with SHA512 if (mInfo.keyBits == 256 && mInfo.cipherAlgorithm == "AES" && @@ -705,6 +719,8 @@ bool AgileEngine::setupEncryption(OUString const & rPassword) setupEncryptionParameters({ 100000, 16, 128, 20, 16, u"AES"_ustr, u"ChainingModeCBC"_ustr, u"SHA1"_ustr }); else if (meEncryptionPreset == AgileEncryptionPreset::AES_128_SHA384) setupEncryptionParameters({ 100000, 16, 128, 48, 16, u"AES"_ustr, u"ChainingModeCBC"_ustr, u"SHA384"_ustr }); + else if (meEncryptionPreset == AgileEncryptionPreset::AES_192_SHA384) + setupEncryptionParameters({ 100000, 16, 192, 48, 16, u"AES"_ustr, u"ChainingModeCBC"_ustr, u"SHA384"_ustr }); else setupEncryptionParameters({ 100000, 16, 256, 64, 16, u"AES"_ustr, u"ChainingModeCBC"_ustr, u"SHA512"_ustr });