diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2018-07-04 22:23:44 +0200 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2018-07-06 18:27:47 +0200 |
commit | ce560ee99ebf97fa44aecedd5110b29913cf77a5 (patch) | |
tree | e228f26944151c51cafd81ca05654ceeeb624046 /oox/qa/unit/CryptoTest.cxx | |
parent | ce7fb7473bc72d8a672c4fdcd49474721c9a2784 (diff) |
oox: Agile encryption and data integrity verification
This adds agile encryption for OOXML documents. Previously we
always used the standard encryption used in MSO 2007 for max.
compatibility, but new MSO versions (2010+) use the agile
encryption, which allows more strong encryption methods (AES256
with SHA512). With this change we can now use do AES128 with
SHA1 or AES256 with SHA512 encryption.
In addition the agile encryption has data verification with HMAC
hashing. With this change we also now write the data verification
hash into the encrypted document and in addition also do data
verification when opening / decrypting a document, so to make sure
the document is not corrupted.
Change-Id: Ib45d397df228c355941eefb76d51e5d6f8925470
Reviewed-on: https://gerrit.libreoffice.org/56974
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'oox/qa/unit/CryptoTest.cxx')
-rw-r--r-- | oox/qa/unit/CryptoTest.cxx | 281 |
1 files changed, 270 insertions, 11 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(); |