diff options
Diffstat (limited to 'oox')
-rw-r--r-- | oox/qa/unit/CryptoTest.cxx | 281 | ||||
-rw-r--r-- | oox/source/core/filterdetect.cxx | 21 | ||||
-rw-r--r-- | oox/source/crypto/AgileEngine.cxx | 481 | ||||
-rw-r--r-- | oox/source/crypto/DocumentDecryption.cxx | 3 | ||||
-rw-r--r-- | oox/source/crypto/DocumentEncryption.cxx | 31 | ||||
-rw-r--r-- | oox/source/crypto/Standard2007Engine.cxx | 39 |
6 files changed, 788 insertions, 68 deletions
diff --git a/oox/qa/unit/CryptoTest.cxx b/oox/qa/unit/CryptoTest.cxx index 0dead9dcec6e..e17f3cc91e9a 100644 --- a/oox/qa/unit/CryptoTest.cxx +++ b/oox/qa/unit/CryptoTest.cxx @@ -15,8 +15,8 @@ #include <tools/stream.hxx> #include <unotools/streamwrap.hxx> -#include <oox/crypto/CryptTools.hxx> #include <oox/crypto/Standard2007Engine.hxx> +#include <oox/crypto/AgileEngine.hxx> #include <oox/helper/binaryinputstream.hxx> #include <oox/helper/binaryoutputstream.hxx> @@ -28,11 +28,19 @@ public: void testCryptoHash(); void testRoundUp(); void testStandard2007(); + void testAgileEncryptionVerifier(); + void testAgileEncrpytionInfoWritingAndParsing(); + void testAgileDataIntegrityHmacKey(); + void testAgileEncryptingAndDecrypting(); CPPUNIT_TEST_SUITE(CryptoTest); CPPUNIT_TEST(testCryptoHash); CPPUNIT_TEST(testRoundUp); CPPUNIT_TEST(testStandard2007); + CPPUNIT_TEST(testAgileEncryptionVerifier); + CPPUNIT_TEST(testAgileEncrpytionInfoWritingAndParsing); + CPPUNIT_TEST(testAgileDataIntegrityHmacKey); + CPPUNIT_TEST(testAgileEncryptingAndDecrypting); CPPUNIT_TEST_SUITE_END(); }; @@ -100,11 +108,13 @@ void CryptoTest::testStandard2007() { oox::core::Standard2007Engine aEngine; { + aEngine.setupEncryption("Password"); + SvMemoryStream aEncryptionInfo; oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream( new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), false); - aEngine.writeEncryptionInfo("Password", aBinaryEncryptionInfoOutputStream); + aEngine.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream); aBinaryEncryptionInfoOutputStream.close(); CPPUNIT_ASSERT_EQUAL(sal_uInt64(224), aEncryptionInfo.GetSize()); @@ -119,17 +129,14 @@ void CryptoTest::testStandard2007() aUnencryptedInput.Seek(STREAM_SEEK_TO_BEGIN); { - oox::BinaryXInputStream aBinaryInputStream( - new utl::OSeekableInputStreamWrapper(aUnencryptedInput), true); - oox::BinaryXOutputStream aBinaryOutputStream( - new utl::OSeekableOutputStreamWrapper(aEncryptedStream), true); + uno::Reference<io::XInputStream> xInputStream( + new utl::OSeekableInputStreamWrapper(aUnencryptedInput)); + uno::Reference<io::XOutputStream> xOutputStream( + new utl::OSeekableOutputStreamWrapper(aEncryptedStream)); - aEncryptedStream.WriteUInt32(aUnencryptedInput.GetSize()); - aEncryptedStream.WriteUInt32(0U); + aEngine.encrypt(xInputStream, xOutputStream, aUnencryptedInput.GetSize()); - aEngine.encrypt(aBinaryInputStream, aBinaryOutputStream); - aBinaryOutputStream.close(); - aBinaryInputStream.close(); + xOutputStream->flush(); const sal_uInt8* pData = static_cast<const sal_uInt8*>(aEncryptedStream.GetData()); sal_uInt64 nSize = aEncryptedStream.GetSize(); @@ -164,6 +171,258 @@ void CryptoTest::testStandard2007() } } +void CryptoTest::testAgileEncryptionVerifier() +{ + oox::core::AgileEngine aEngine; + + OUString aPassword("Password"); + + aEngine.setupEncryptionParameters({ 100000, 16, 128, 20, 16, OUString("AES"), + OUString("ChainingModeCBC"), OUString("SHA1") }); + + 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") }); + + CPPUNIT_ASSERT_EQUAL(true, aEngine.generateAndEncryptVerifierHash(aPassword)); + CPPUNIT_ASSERT_EQUAL(false, aEngine.decryptAndCheckVerifierHash("Wrong")); + CPPUNIT_ASSERT_EQUAL(true, aEngine.decryptAndCheckVerifierHash(aPassword)); +} + +void CryptoTest::testAgileEncrpytionInfoWritingAndParsing() +{ + OUString aPassword("Password"); + std::vector<sal_uInt8> aKeyDataSalt; + + { // Preset AES128 - SHA1 + SvMemoryStream aEncryptionInfo; + { + oox::core::AgileEngine aEngine; + + aEngine.setPreset(oox::core::AgileEncryptionPreset::AES_128_SHA1); + 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(996), 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 + xInputStream->skipBytes(4); // Reserved + + 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(20), 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("SHA1"), 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; + { + oox::core::AgileEngine aEngine; + + aEngine.setPreset(oox::core::AgileEncryptionPreset::AES_256_SHA512); + 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(1112), 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 + xInputStream->skipBytes(4); // Reserved + + 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(256), rInfo.keyBits); + CPPUNIT_ASSERT_EQUAL(sal_Int32(64), 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("SHA512"), rInfo.hashAlgorithm); + CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt), toString(rInfo.keyDataSalt)); + + CPPUNIT_ASSERT_EQUAL(false, aEngine.decryptAndCheckVerifierHash("Wrong")); + CPPUNIT_ASSERT_EQUAL(true, aEngine.decryptAndCheckVerifierHash(aPassword)); + } + } +} + +void CryptoTest::testAgileDataIntegrityHmacKey() +{ + OUString aPassword("Password"); + + std::vector<sal_uInt8> aKeyDataSalt; + + std::vector<sal_uInt8> aHmacKey; + std::vector<sal_uInt8> aHmacEncryptedKey; + + SvMemoryStream aEncryptionInfo; + { + oox::core::AgileEngine aEngine; + aEngine.setupEncryption(aPassword); + oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream( + new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), true); + aEngine.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream); + aBinaryEncryptionInfoOutputStream.close(); + + aHmacKey = aEngine.getInfo().hmacKey; + aKeyDataSalt = aEngine.getInfo().keyDataSalt; + aHmacEncryptedKey = aEngine.getInfo().hmacEncryptedKey; + } + + 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 + xInputStream->skipBytes(4); // Reserved + + CPPUNIT_ASSERT(aEngine.readEncryptionInfo(xInputStream)); + CPPUNIT_ASSERT(aEngine.generateEncryptionKey(aPassword)); + + CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt), toString(aEngine.getInfo().keyDataSalt)); + + CPPUNIT_ASSERT_EQUAL(toString(aHmacEncryptedKey), + toString(aEngine.getInfo().hmacEncryptedKey)); + + CPPUNIT_ASSERT_EQUAL(size_t(64), aHmacKey.size()); + CPPUNIT_ASSERT_EQUAL(toString(aHmacKey), toString(aEngine.getInfo().hmacKey)); + } +} + +void CryptoTest::testAgileEncryptingAndDecrypting() +{ + OUString aPassword("Password"); + + SvMemoryStream aEncryptionInfo; + SvMemoryStream aEncryptedStream; + + OString aTestString = OUStringToOString("1234567890ABCDEFGH", RTL_TEXTENCODING_UTF8); + + { + oox::core::AgileEngine aEngine; + + // Setup input + SvMemoryStream aUnencryptedInput; + uno::Reference<io::XInputStream> xInputStream( + new utl::OSeekableInputStreamWrapper(aUnencryptedInput)); + + aUnencryptedInput.WriteBytes(aTestString.getStr(), aTestString.getLength() + 1); + aUnencryptedInput.Seek(STREAM_SEEK_TO_BEGIN); + + // Setup output + uno::Reference<io::XOutputStream> xOutputStream( + new utl::OSeekableOutputStreamWrapper(aEncryptedStream)); + + // Write content + aEngine.setupEncryption(aPassword); + aEngine.encrypt(xInputStream, xOutputStream, aUnencryptedInput.GetSize()); + xOutputStream->flush(); + + // Check content + sal_uInt64 nSize = aEncryptedStream.GetSize(); + + CPPUNIT_ASSERT_EQUAL(sal_uInt64(40), nSize); + + // Setup and write encryption info + oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream( + new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), true); + aEngine.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream); + aBinaryEncryptionInfoOutputStream.close(); + } + + aEncryptedStream.Seek(STREAM_SEEK_TO_BEGIN); + aEncryptionInfo.Seek(STREAM_SEEK_TO_BEGIN); + + { + oox::core::AgileEngine aEngine; + + // Read encryption info + uno::Reference<io::XInputStream> xEncryptionInfo( + new utl::OSeekableInputStreamWrapper(aEncryptionInfo)); + + xEncryptionInfo->skipBytes(4); // Encryption type -> Agile + xEncryptionInfo->skipBytes(4); // Reserved + + CPPUNIT_ASSERT(aEngine.readEncryptionInfo(xEncryptionInfo)); + + // Setup password + CPPUNIT_ASSERT(aEngine.generateEncryptionKey(aPassword)); + + // Setup encrypted input stream + oox::BinaryXInputStream aBinaryInputStream( + new utl::OSeekableInputStreamWrapper(aEncryptedStream), true); + + // Setup output stream + SvMemoryStream aUnencryptedOutput; + oox::BinaryXOutputStream aBinaryOutputStream( + new utl::OSeekableOutputStreamWrapper(aUnencryptedOutput), true); + + // Decrypt + aEngine.decrypt(aBinaryInputStream, aBinaryOutputStream); + aBinaryOutputStream.close(); + aBinaryInputStream.close(); + + // Check decrypted output + CPPUNIT_ASSERT_EQUAL(sal_uInt64(19), aUnencryptedOutput.GetSize()); + + OString aString(static_cast<const sal_Char*>(aUnencryptedOutput.GetData())); + CPPUNIT_ASSERT_EQUAL(aTestString, aString); + + // Check data integrity + CPPUNIT_ASSERT_EQUAL(true, aEngine.checkDataIntegrity()); + } +} + CPPUNIT_TEST_SUITE_REGISTRATION(CryptoTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/oox/source/core/filterdetect.cxx b/oox/source/core/filterdetect.cxx index 1af982c07480..805d2fb4b01d 100644 --- a/oox/source/core/filterdetect.cxx +++ b/oox/source/core/filterdetect.cxx @@ -348,14 +348,21 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript { // create temporary file for unencrypted package Reference<XStream> xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW ); - aDecryptor.decrypt( xTempFile ); - // store temp file in media descriptor to keep it alive - rMediaDescriptor.setComponentDataEntry( "DecryptedPackage", Any( xTempFile ) ); - - Reference<XInputStream> xDecryptedInputStream = xTempFile->getInputStream(); - if( lclIsZipPackage( mxContext, xDecryptedInputStream ) ) - return xDecryptedInputStream; + // if decryption was unsuccessful (corrupted file or any other reason) + if (!aDecryptor.decrypt(xTempFile)) + { + rMediaDescriptor[ MediaDescriptor::PROP_ABORTED() ] <<= true; + } + else + { + // store temp file in media descriptor to keep it alive + rMediaDescriptor.setComponentDataEntry( "DecryptedPackage", Any( xTempFile ) ); + + Reference<XInputStream> xDecryptedInputStream = xTempFile->getInputStream(); + if( lclIsZipPackage( mxContext, xDecryptedInputStream ) ) + return xDecryptedInputStream; + } } } } diff --git a/oox/source/crypto/AgileEngine.cxx b/oox/source/crypto/AgileEngine.cxx index 72cd51e16986..a4fa8c476c74 100644 --- a/oox/source/crypto/AgileEngine.cxx +++ b/oox/source/crypto/AgileEngine.cxx @@ -23,6 +23,7 @@ #include <comphelper/sequence.hxx> #include <filter/msfilter/mscodec.hxx> +#include <tools/XmlWriter.hxx> #include <com/sun/star/io/XSeekable.hpp> #include <com/sun/star/io/XStream.hpp> @@ -146,6 +147,18 @@ public: comphelper::Base64::decode(encryptedKeyValue, rAttribute.Value); mInfo.encryptedKeyValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedKeyValue); } + if (rAttrLocalName == "encryptedHmacKey") + { + Sequence<sal_Int8> aValue; + comphelper::Base64::decode(aValue, rAttribute.Value); + mInfo.hmacEncryptedKey = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(aValue); + } + if (rAttrLocalName == "encryptedHmacValue") + { + Sequence<sal_Int8> aValue; + comphelper::Base64::decode(aValue, rAttribute.Value); + mInfo.hmacEncryptedValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(aValue); + } } } @@ -168,7 +181,13 @@ public: {} }; -const sal_uInt32 constSegmentLength = 4096; +constexpr const sal_uInt32 constSegmentLength = 4096; + +static const std::vector<sal_uInt8> constBlock1 { 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79 }; +static const std::vector<sal_uInt8> constBlock2 { 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e }; +static const std::vector<sal_uInt8> constBlock3 { 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6 }; +static const std::vector<sal_uInt8> constBlockHmac1 { 0x5f, 0xb2, 0xad, 0x01, 0x0c, 0xb9, 0xe1, 0xf6 }; +static const std::vector<sal_uInt8> constBlockHmac2 { 0xa0, 0x67, 0x7f, 0x02, 0xb2, 0x2c, 0x84, 0x33 }; bool hashCalc(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, @@ -189,8 +208,19 @@ bool hashCalc(std::vector<sal_uInt8>& output, return false; } +CryptoHashType cryptoHashTypeFromString(OUString const & sAlgorithm) +{ + if (sAlgorithm == "SHA512") + return CryptoHashType::SHA512; + return CryptoHashType::SHA1; +} + } // namespace +AgileEngine::AgileEngine() + : meEncryptionPreset(AgileEncryptionPreset::AES_256_SHA512) +{} + Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo) { if (rInfo.keyBits == 128 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC") @@ -200,6 +230,19 @@ Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo) return Crypto::UNKNOWN; } +std::vector<sal_uInt8> calculateIV(comphelper::HashType eType, + std::vector<sal_uInt8> const & rSalt, + std::vector<sal_uInt8> const & rBlock, + sal_Int32 nCipherBlockSize) +{ + comphelper::Hash aHasher(eType); + aHasher.update(rSalt.data(), rSalt.size()); + aHasher.update(rBlock.data(), rBlock.size()); + std::vector<sal_uInt8> aIV = aHasher.finalize(); + aIV.resize(roundUp(sal_Int32(aIV.size()), nCipherBlockSize), 0x36); + return aIV; +} + void AgileEngine::calculateBlock( std::vector<sal_uInt8> const & rBlock, std::vector<sal_uInt8>& rHashFinal, @@ -222,21 +265,55 @@ void AgileEngine::calculateBlock( aDecryptor.update(rOutput, rInput); } +void AgileEngine::encryptBlock( + std::vector<sal_uInt8> const & rBlock, + std::vector<sal_uInt8> & rHashFinal, + std::vector<sal_uInt8> & rInput, + std::vector<sal_uInt8> & rOutput) +{ + std::vector<sal_uInt8> hash(mInfo.hashSize, 0); + std::vector<sal_uInt8> dataFinal(mInfo.hashSize + rBlock.size(), 0); + std::copy(rHashFinal.begin(), rHashFinal.end(), dataFinal.begin()); + std::copy(rBlock.begin(), rBlock.end(), dataFinal.begin() + mInfo.hashSize); + + hashCalc(hash, dataFinal, mInfo.hashAlgorithm); + + sal_Int32 keySize = mInfo.keyBits / 8; + std::vector<sal_uInt8> key(keySize, 0); + + std::copy(hash.begin(), hash.begin() + keySize, key.begin()); + + Encrypt aEncryptor(key, mInfo.saltValue, cryptoType(mInfo)); + + aEncryptor.update(rOutput, rInput); +} + void AgileEngine::calculateHashFinal(const OUString& rPassword, std::vector<sal_uInt8>& aHashFinal) { - aHashFinal = comphelper::DocPasswordHelper::GetOoxHashAsVector( rPassword, mInfo.saltValue, - mInfo.spinCount, comphelper::Hash::IterCount::PREPEND, mInfo.hashAlgorithm); + aHashFinal = comphelper::DocPasswordHelper::GetOoxHashAsVector( + rPassword, mInfo.saltValue, mInfo.spinCount, + comphelper::Hash::IterCount::PREPEND, mInfo.hashAlgorithm); } -bool AgileEngine::generateEncryptionKey(const OUString& rPassword) +namespace { - static const std::vector<sal_uInt8> constBlock1{ 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79 }; - static const std::vector<sal_uInt8> constBlock2{ 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e }; - static const std::vector<sal_uInt8> constBlock3{ 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6 }; - mKey.clear(); - mKey.resize(mInfo.keyBits / 8, 0); +bool generateBytes(std::vector<sal_uInt8> & rBytes, sal_Int32 nSize) +{ + size_t nMax = std::min(rBytes.size(), size_t(nSize)); + + for (size_t i = 0; i < nMax; ++i) + { + rBytes[i] = sal_uInt8(comphelper::rng::uniform_uint_distribution(0, 0xFF)); + } + return true; +} + +} // end anonymous namespace + +bool AgileEngine::decryptAndCheckVerifierHash(OUString const & rPassword) +{ std::vector<sal_uInt8> hashFinal(mInfo.hashSize, 0); calculateHashFinal(rPassword, hashFinal); @@ -251,22 +328,114 @@ bool AgileEngine::generateEncryptionKey(const OUString& rPassword) std::vector<sal_uInt8> hash(mInfo.hashSize, 0); hashCalc(hash, hashInput, mInfo.hashAlgorithm); - if (hash.size() <= hashValue.size() && std::equal(hash.begin(), hash.end(), hashValue.begin())) + return (hash.size() <= hashValue.size() && std::equal(hash.begin(), hash.end(), hashValue.begin())); +} + +bool AgileEngine::decryptEncryptionKey(OUString const & rPassword) +{ + sal_Int32 nKeySize = mInfo.keyBits / 8; + + mKey.clear(); + mKey.resize(nKeySize, 0); + + std::vector<sal_uInt8> aPasswordHash(mInfo.hashSize, 0); + calculateHashFinal(rPassword, aPasswordHash); + + calculateBlock(constBlock3, aPasswordHash, mInfo.encryptedKeyValue, mKey); + + return true; +} + +// TODO: Rename +bool AgileEngine::generateEncryptionKey(OUString const & rPassword) +{ + bool bResult = decryptAndCheckVerifierHash(rPassword); + + if (bResult) { - std::vector<sal_uInt8>& encryptedKeyValue = mInfo.encryptedKeyValue; - calculateBlock(constBlock3, hashFinal, encryptedKeyValue, mKey); - return true; + decryptEncryptionKey(rPassword); + decryptHmacKey(); + decryptHmacValue(); } + return bResult; +} - return false; +bool AgileEngine::decryptHmacKey() +{ + // Initialize hmacKey + mInfo.hmacKey.clear(); + mInfo.hmacKey.resize(mInfo.hmacEncryptedKey.size(), 0); + + // Calculate IV + comphelper::HashType eType; + if (mInfo.hashAlgorithm == "SHA1") + eType = comphelper::HashType::SHA1; + else if (mInfo.hashAlgorithm == "SHA512") + eType = comphelper::HashType::SHA512; + else + return false; + + std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac1, mInfo.blockSize); + + // Decrypt with out key, calculated iv + Decrypt aDecrypt(mKey, iv, cryptoType(mInfo)); + aDecrypt.update(mInfo.hmacKey, mInfo.hmacEncryptedKey); + + mInfo.hmacKey.resize(mInfo.hashSize, 0); + + return true; +} + +bool AgileEngine::decryptHmacValue() +{ + // Initialize hmacHash + mInfo.hmacHash.clear(); + mInfo.hmacHash.resize(mInfo.hmacEncryptedValue.size(), 0); + + // Calculate IV + comphelper::HashType eType; + if (mInfo.hashAlgorithm == "SHA1") + eType = comphelper::HashType::SHA1; + else if (mInfo.hashAlgorithm == "SHA512") + eType = comphelper::HashType::SHA512; + else + return false; + std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac2, mInfo.blockSize); + + // Decrypt with out key, calculated iv + Decrypt aDecrypt(mKey, iv, cryptoType(mInfo)); + aDecrypt.update(mInfo.hmacHash, mInfo.hmacEncryptedValue); + + mInfo.hmacHash.resize(mInfo.hashSize, 0); + + return true; +} + +bool AgileEngine::checkDataIntegrity() +{ + bool bResult = (mInfo.hmacHash.size() == mInfo.hmacCalculatedHash.size() && + std::equal(mInfo.hmacHash.begin(), mInfo.hmacHash.end(), mInfo.hmacCalculatedHash.begin())); + + return bResult; } bool AgileEngine::decrypt(BinaryXInputStream& aInputStream, BinaryXOutputStream& aOutputStream) { + CryptoHash aCryptoHash(mInfo.hmacKey, cryptoHashTypeFromString(mInfo.hashAlgorithm)); + sal_uInt32 totalSize = aInputStream.readuInt32(); // Document unencrypted size - 4 bytes + // account for size in HMAC + std::vector<sal_uInt8> aSizeBytes(sizeof(sal_uInt32)); + ByteOrderConverter::writeLittleEndian(aSizeBytes.data(), totalSize); + aCryptoHash.update(aSizeBytes); + aInputStream.skip(4); // Reserved 4 Bytes + // accout for reserved 4 bytes (must be 0) + std::vector<sal_uInt8> aReserved{0,0,0,0}; + aCryptoHash.update(aReserved); + // setup decryption std::vector<sal_uInt8>& keyDataSalt = mInfo.keyDataSalt; sal_uInt32 saltSize = mInfo.saltSize; @@ -286,7 +455,7 @@ bool AgileEngine::decrypt(BinaryXInputStream& aInputStream, sal_uInt32 outputLength; sal_uInt32 remaining = totalSize; - while ((inputLength = aInputStream.readMemory(inputBuffer.data(), constSegmentLength)) > 0) + while ((inputLength = aInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0) { sal_uInt8* segmentBegin = reinterpret_cast<sal_uInt8*>(&segment); sal_uInt8* segmentEnd = segmentBegin + sizeof(segment); @@ -301,12 +470,17 @@ bool AgileEngine::decrypt(BinaryXInputStream& aInputStream, outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength); sal_uInt32 writeLength = std::min(outputLength, remaining); + + aCryptoHash.update(inputBuffer, inputLength); + aOutputStream.writeMemory(outputBuffer.data(), writeLength); remaining -= outputLength; segment++; } + mInfo.hmacCalculatedHash = aCryptoHash.finalize(); + return true; } @@ -363,18 +537,279 @@ bool AgileEngine::readEncryptionInfo(uno::Reference<io::XInputStream> & rxInputS return false; } -void AgileEngine::writeEncryptionInfo( - const OUString& /*aPassword*/, - BinaryXOutputStream& /*rStream*/) +bool AgileEngine::generateAndEncryptVerifierHash(OUString const & rPassword) +{ + if (!generateBytes(mInfo.saltValue, mInfo.saltSize)) + return false; + + std::vector<sal_uInt8> unencryptedVerifierHashInput(mInfo.saltSize); + if (!generateBytes(unencryptedVerifierHashInput, mInfo.saltSize)) + return false; + + // HASH - needs to be modified to be multiple of block size + sal_Int32 nVerifierHash = roundUp(mInfo.hashSize, mInfo.blockSize); + std::vector<sal_uInt8> unencryptedVerifierHashValue; + if (!hashCalc(unencryptedVerifierHashValue, unencryptedVerifierHashInput, mInfo.hashAlgorithm)) + return false; + unencryptedVerifierHashValue.resize(nVerifierHash, 0); + + std::vector<sal_uInt8> hashFinal(mInfo.hashSize, 0); + calculateHashFinal(rPassword, hashFinal); + + encryptBlock(constBlock1, hashFinal, unencryptedVerifierHashInput, mInfo.encryptedVerifierHashInput); + + encryptBlock(constBlock2, hashFinal, unencryptedVerifierHashValue, mInfo.encryptedVerifierHashValue); + + return true; +} + +bool AgileEngine::encryptHmacKey() +{ + // Initialize hmacKey + mInfo.hmacKey.clear(); + mInfo.hmacKey.resize(mInfo.hashSize, 0); + + if (!generateBytes(mInfo.hmacKey, mInfo.hashSize)) + return false; + + // Encrypted salt must be multiple of block size + sal_Int32 nEncryptedSaltSize = oox::core::roundUp(mInfo.hashSize, mInfo.blockSize); + + // We need to extend hmacSalt to multiple of block size, padding with 0x36 + std::vector<sal_uInt8> extendedSalt(mInfo.hmacKey); + extendedSalt.resize(nEncryptedSaltSize, 0x36); + + // Initialize hmacEncryptedKey + mInfo.hmacEncryptedKey.clear(); + mInfo.hmacEncryptedKey.resize(nEncryptedSaltSize, 0); + + // Calculate IV + comphelper::HashType eType; + if (mInfo.hashAlgorithm == "SHA1") + eType = comphelper::HashType::SHA1; + else if (mInfo.hashAlgorithm == "SHA512") + eType = comphelper::HashType::SHA512; + else + return false; + + std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac1, mInfo.blockSize); + + // Encrypt with out key, calculated iv + Encrypt aEncryptor(mKey, iv, cryptoType(mInfo)); + aEncryptor.update(mInfo.hmacEncryptedKey, extendedSalt); + + return true; +} + +bool AgileEngine::encryptHmacValue() +{ + sal_Int32 nEncryptedValueSize = roundUp(mInfo.hashSize, mInfo.blockSize); + mInfo.hmacEncryptedValue.clear(); + mInfo.hmacEncryptedValue.resize(nEncryptedValueSize, 0); + + std::vector<sal_uInt8> extendedHash(mInfo.hmacHash); + extendedHash.resize(nEncryptedValueSize, 0x36); + + // Calculate IV + comphelper::HashType eType; + if (mInfo.hashAlgorithm == "SHA1") + eType = comphelper::HashType::SHA1; + else if (mInfo.hashAlgorithm == "SHA512") + eType = comphelper::HashType::SHA512; + else + return false; + + std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac2, mInfo.blockSize); + + // Encrypt with out key, calculated iv + Encrypt aEncryptor(mKey, iv, cryptoType(mInfo)); + aEncryptor.update(mInfo.hmacEncryptedValue, extendedHash); + + return true; +} + +bool AgileEngine::encryptEncryptionKey(OUString const & rPassword) +{ + sal_Int32 nKeySize = mInfo.keyBits / 8; + + mKey.clear(); + mKey.resize(nKeySize, 0); + + mInfo.encryptedKeyValue.clear(); + mInfo.encryptedKeyValue.resize(nKeySize, 0); + + if (!generateBytes(mKey, nKeySize)) + return false; + + std::vector<sal_uInt8> aPasswordHash(mInfo.hashSize, 0); + calculateHashFinal(rPassword, aPasswordHash); + + encryptBlock(constBlock3, aPasswordHash, mKey, mInfo.encryptedKeyValue); + + return true; +} + +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 + setupEncryptionParameters({ 100000, 16, 256, 64, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA512") }); + + if (!setupEncryptionKey(rPassword)) + return false; + return true; +} + +void AgileEngine::setupEncryptionParameters(AgileEncryptionParameters const & rAgileEncryptionParameters) +{ + mInfo.spinCount = rAgileEncryptionParameters.spinCount; + mInfo.saltSize = rAgileEncryptionParameters.saltSize; + mInfo.keyBits = rAgileEncryptionParameters.keyBits; + mInfo.hashSize = rAgileEncryptionParameters.hashSize; + mInfo.blockSize = rAgileEncryptionParameters.blockSize; + + mInfo.cipherAlgorithm = rAgileEncryptionParameters.cipherAlgorithm; + mInfo.cipherChaining = rAgileEncryptionParameters.cipherChaining; + mInfo.hashAlgorithm = rAgileEncryptionParameters.hashAlgorithm; + + mInfo.keyDataSalt.resize(mInfo.saltSize); + mInfo.saltValue.resize(mInfo.saltSize); + mInfo.encryptedVerifierHashInput.resize(mInfo.saltSize); + mInfo.encryptedVerifierHashValue.resize(roundUp(mInfo.hashSize, mInfo.blockSize), 0); +} + +bool AgileEngine::setupEncryptionKey(OUString const & rPassword) { - // Agile encrypting is not supported for now + if (!generateAndEncryptVerifierHash(rPassword)) + return false; + if (!encryptEncryptionKey(rPassword)) + return false; + if (!generateBytes(mInfo.keyDataSalt, mInfo.saltSize)) + return false; + if (!encryptHmacKey()) + return false; + return true; } -void AgileEngine::encrypt( - BinaryXInputStream& /*aInputStream*/, - BinaryXOutputStream& /*aOutputStream*/) +void AgileEngine::writeEncryptionInfo(BinaryXOutputStream & rStream) { - // Agile encrypting is not supported for now + rStream.WriteUInt32(msfilter::VERSION_INFO_AGILE); + rStream.WriteUInt32(0); // reserved + + SvMemoryStream aMemStream; + tools::XmlWriter aXmlWriter(&aMemStream); + + if (aXmlWriter.startDocument(0/*nIndent*/)) + { + aXmlWriter.startElement("", "encryption", "http://schemas.microsoft.com/office/2006/encryption"); + aXmlWriter.attribute("xmlns:p", OString("http://schemas.microsoft.com/office/2006/keyEncryptor/password")); + + aXmlWriter.startElement("keyData"); + aXmlWriter.attribute("saltSize", mInfo.saltSize); + aXmlWriter.attribute("blockSize", mInfo.blockSize); + aXmlWriter.attribute("keyBits", mInfo.keyBits); + aXmlWriter.attribute("hashSize", mInfo.hashSize); + aXmlWriter.attribute("cipherAlgorithm", mInfo.cipherAlgorithm); + aXmlWriter.attribute("cipherChaining", mInfo.cipherChaining); + aXmlWriter.attribute("hashAlgorithm", mInfo.hashAlgorithm); + aXmlWriter.attributeBase64("saltValue", mInfo.keyDataSalt); + aXmlWriter.endElement(); + + aXmlWriter.startElement("dataIntegrity"); + aXmlWriter.attributeBase64("encryptedHmacKey", mInfo.hmacEncryptedKey); + aXmlWriter.attributeBase64("encryptedHmacValue", mInfo.hmacEncryptedValue); + aXmlWriter.endElement(); + + aXmlWriter.startElement("keyEncryptors"); + aXmlWriter.startElement("keyEncryptor"); + aXmlWriter.attribute("uri", OString("http://schemas.microsoft.com/office/2006/keyEncryptor/password")); + + aXmlWriter.startElement("p", "encryptedKey", ""); + aXmlWriter.attribute("spinCount", mInfo.spinCount); + aXmlWriter.attribute("saltSize", mInfo.saltSize); + aXmlWriter.attribute("blockSize", mInfo.blockSize); + aXmlWriter.attribute("keyBits", mInfo.keyBits); + aXmlWriter.attribute("hashSize", mInfo.hashSize); + aXmlWriter.attribute("cipherAlgorithm", mInfo.cipherAlgorithm); + aXmlWriter.attribute("cipherChaining", mInfo.cipherChaining); + aXmlWriter.attribute("hashAlgorithm", mInfo.hashAlgorithm); + aXmlWriter.attributeBase64("saltValue", mInfo.saltValue); + aXmlWriter.attributeBase64("encryptedVerifierHashInput", mInfo.encryptedVerifierHashInput); + aXmlWriter.attributeBase64("encryptedVerifierHashValue", mInfo.encryptedVerifierHashValue); + aXmlWriter.attributeBase64("encryptedKeyValue", mInfo.encryptedKeyValue); + aXmlWriter.endElement(); + + aXmlWriter.endElement(); + aXmlWriter.endElement(); + + aXmlWriter.endElement(); + aXmlWriter.endDocument(); + } + rStream.writeMemory(aMemStream.GetData(), aMemStream.GetSize()); +} + +void AgileEngine::encrypt(css::uno::Reference<css::io::XInputStream> & rxInputStream, + css::uno::Reference<css::io::XOutputStream> & rxOutputStream, + sal_uInt32 nSize) +{ + CryptoHash aCryptoHash(mInfo.hmacKey, cryptoHashTypeFromString(mInfo.hashAlgorithm)); + + BinaryXOutputStream aBinaryOutputStream(rxOutputStream, false); + BinaryXInputStream aBinaryInputStream(rxInputStream, false); + + std::vector<sal_uInt8> aSizeBytes(sizeof(sal_uInt32)); + ByteOrderConverter::writeLittleEndian(aSizeBytes.data(), nSize); + aBinaryOutputStream.writeMemory(aSizeBytes.data(), aSizeBytes.size()); // size + aCryptoHash.update(aSizeBytes, aSizeBytes.size()); + + std::vector<sal_uInt8> aNull{0,0,0,0}; + aBinaryOutputStream.writeMemory(aNull.data(), aNull.size()); // reserved + aCryptoHash.update(aNull, aNull.size()); + + std::vector<sal_uInt8>& keyDataSalt = mInfo.keyDataSalt; + + sal_uInt32 saltSize = mInfo.saltSize; + sal_uInt32 keySize = mInfo.keyBits / 8; + + sal_uInt32 nSegment = 0; + sal_uInt32 nSegmentByteSize = sizeof(nSegment); + + std::vector<sal_uInt8> saltWithBlockKey(saltSize + nSegmentByteSize, 0); + std::copy(keyDataSalt.begin(), keyDataSalt.end(), saltWithBlockKey.begin()); + + std::vector<sal_uInt8> hash(mInfo.hashSize, 0); + std::vector<sal_uInt8> iv(keySize, 0); + + std::vector<sal_uInt8> inputBuffer(constSegmentLength); + std::vector<sal_uInt8> outputBuffer(constSegmentLength); + sal_uInt32 inputLength; + sal_uInt32 outputLength; + + while ((inputLength = aBinaryInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0) + { + sal_uInt32 correctedInputLength = inputLength % mInfo.blockSize == 0 ? + inputLength : oox::core::roundUp(inputLength, sal_uInt32(mInfo.blockSize)); + + // Update Key + sal_uInt8* segmentBegin = reinterpret_cast<sal_uInt8*>(&nSegment); + sal_uInt8* segmentEnd = segmentBegin + nSegmentByteSize; + std::copy(segmentBegin, segmentEnd, saltWithBlockKey.begin() + saltSize); + + hashCalc(hash, saltWithBlockKey, mInfo.hashAlgorithm); + + // Only if hash > keySize + std::copy(hash.begin(), hash.begin() + keySize, iv.begin()); + + Encrypt aEncryptor(mKey, iv, AgileEngine::cryptoType(mInfo)); + outputLength = aEncryptor.update(outputBuffer, inputBuffer, correctedInputLength); + aBinaryOutputStream.writeMemory(outputBuffer.data(), outputLength); + aCryptoHash.update(outputBuffer, outputLength); + + nSegment++; + } + mInfo.hmacHash = aCryptoHash.finalize(); + encryptHmacValue(); } } // namespace core diff --git a/oox/source/crypto/DocumentDecryption.cxx b/oox/source/crypto/DocumentDecryption.cxx index 40d854123568..b68882ad6b03 100644 --- a/oox/source/crypto/DocumentDecryption.cxx +++ b/oox/source/crypto/DocumentDecryption.cxx @@ -106,6 +106,9 @@ bool DocumentDecryption::decrypt(const uno::Reference<io::XStream>& xDocumentStr xDecryptedPackage->flush(); aDecryptedPackage.seekToStart(); + if (bResult) + return mEngine->checkDataIntegrity(); + return bResult; } diff --git a/oox/source/crypto/DocumentEncryption.cxx b/oox/source/crypto/DocumentEncryption.cxx index d8b783ac2d41..8cb62c23f006 100644 --- a/oox/source/crypto/DocumentEncryption.cxx +++ b/oox/source/crypto/DocumentEncryption.cxx @@ -41,37 +41,30 @@ bool DocumentEncryption::encrypt() if (!xSeekable.is()) return false; - sal_uInt32 aLength = xSeekable->getLength(); + sal_uInt32 aLength = xSeekable->getLength(); // check length of the stream + xSeekable->seek(0); // seek to begin of the document stream if (!mrOleStorage.isStorage()) return false; + mEngine.setupEncryption(maPassword); + + Reference<XOutputStream> xOutputStream(mrOleStorage.openOutputStream("EncryptedPackage"), UNO_SET_THROW); + + mEngine.encrypt(xInputStream, xOutputStream, aLength); + + xOutputStream->flush(); + xOutputStream->closeOutput(); + Reference<XOutputStream> xEncryptionInfo(mrOleStorage.openOutputStream("EncryptionInfo"), UNO_SET_THROW); BinaryXOutputStream aEncryptionInfoBinaryOutputStream(xEncryptionInfo, false); - mEngine.writeEncryptionInfo(maPassword, aEncryptionInfoBinaryOutputStream); + mEngine.writeEncryptionInfo(aEncryptionInfoBinaryOutputStream); aEncryptionInfoBinaryOutputStream.close(); xEncryptionInfo->flush(); xEncryptionInfo->closeOutput(); - Reference<XOutputStream> xEncryptedPackage(mrOleStorage.openOutputStream("EncryptedPackage"), UNO_SET_THROW); - BinaryXOutputStream aEncryptedPackageStream(xEncryptedPackage, false); - - BinaryXInputStream aDocumentInputStream(xInputStream, false); - aDocumentInputStream.seekToStart(); - - aEncryptedPackageStream.WriteUInt32(aLength); // size - aEncryptedPackageStream.WriteUInt32(0U); // reserved - - mEngine.encrypt(aDocumentInputStream, aEncryptedPackageStream); - - aEncryptedPackageStream.close(); - aDocumentInputStream.close(); - - xEncryptedPackage->flush(); - xEncryptedPackage->closeOutput(); - return true; } diff --git a/oox/source/crypto/Standard2007Engine.cxx b/oox/source/crypto/Standard2007Engine.cxx index d1d92269d96c..6dd3e758b641 100644 --- a/oox/source/crypto/Standard2007Engine.cxx +++ b/oox/source/crypto/Standard2007Engine.cxx @@ -35,6 +35,7 @@ void lclRandomGenerateValues(sal_uInt8* aArray, sal_uInt32 aSize) } static const OUString lclCspName = "Microsoft Enhanced RSA and AES Cryptographic Provider"; +constexpr const sal_uInt32 AES128Size = 16; } // end anonymous namespace @@ -172,7 +173,12 @@ bool Standard2007Engine::decrypt(BinaryXInputStream& aInputStream, return true; } -void Standard2007Engine::writeEncryptionInfo(const OUString& password, BinaryXOutputStream& rStream) +bool Standard2007Engine::checkDataIntegrity() +{ + return true; +} + +bool Standard2007Engine::setupEncryption(OUString const & password) { mInfo.header.flags = msfilter::ENCRYPTINFO_AES | msfilter::ENCRYPTINFO_CRYPTOAPI; mInfo.header.algId = msfilter::ENCRYPT_ALGO_AES128; @@ -187,11 +193,16 @@ void Standard2007Engine::writeEncryptionInfo(const OUString& password, BinaryXOu mKey.resize(keyLength, 0); if (!calculateEncryptionKey(password)) - return; + return false; if (!generateVerifier()) - return; + return false; + return true; +} + +void Standard2007Engine::writeEncryptionInfo(BinaryXOutputStream& rStream) +{ rStream.WriteUInt32(msfilter::VERSION_INFO_2007_FORMAT); sal_uInt32 cspNameSize = (lclCspName.getLength() * 2) + 2; @@ -209,9 +220,19 @@ void Standard2007Engine::writeEncryptionInfo(const OUString& password, BinaryXOu rStream.writeMemory(&mInfo.verifier, sizeof(msfilter::EncryptionVerifierAES)); } -void Standard2007Engine::encrypt(BinaryXInputStream& aInputStream, - BinaryXOutputStream& aOutputStream) +void Standard2007Engine::encrypt(css::uno::Reference<css::io::XInputStream> & rxInputStream, + css::uno::Reference<css::io::XOutputStream> & rxOutputStream, + sal_uInt32 nSize) { + if (mKey.empty()) + return; + + BinaryXOutputStream aBinaryOutputStream(rxOutputStream, false); + BinaryXInputStream aBinaryInputStream(rxInputStream, false); + + aBinaryOutputStream.WriteUInt32(nSize); // size + aBinaryOutputStream.WriteUInt32(0U); // reserved + std::vector<sal_uInt8> inputBuffer(1024); std::vector<sal_uInt8> outputBuffer(1024); @@ -221,11 +242,13 @@ void Standard2007Engine::encrypt(BinaryXInputStream& aInputStream, std::vector<sal_uInt8> iv; Encrypt aEncryptor(mKey, iv, Crypto::AES_128_ECB); - while ((inputLength = aInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0) + while ((inputLength = aBinaryInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0) { - inputLength = inputLength % 16 == 0 ? inputLength : ((inputLength / 16) * 16) + 16; + // increase size to multiple of 16 (size of mKey) if necessary + inputLength = inputLength % AES128Size == 0 ? + inputLength : roundUp(inputLength, AES128Size); outputLength = aEncryptor.update(outputBuffer, inputBuffer, inputLength); - aOutputStream.writeMemory(outputBuffer.data(), outputLength); + aBinaryOutputStream.writeMemory(outputBuffer.data(), outputLength); } } |