Kaydet (Commit) ce560ee9 authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl Kaydeden (comit) Tomaž Vajngerl

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: 's avatarTomaž Vajngerl <quikee@gmail.com>
üst ce7fb747
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <vector> #include <vector>
#include <oox/dllapi.h>
#include <oox/crypto/CryptTools.hxx> #include <oox/crypto/CryptTools.hxx>
#include <oox/crypto/CryptoEngine.hxx> #include <oox/crypto/CryptoEngine.hxx>
#include <rtl/ustring.hxx> #include <rtl/ustring.hxx>
...@@ -26,7 +27,7 @@ namespace oox { ...@@ -26,7 +27,7 @@ namespace oox {
namespace oox { namespace oox {
namespace core { namespace core {
struct AgileEncryptionInfo struct OOX_DLLPUBLIC AgileEncryptionInfo
{ {
sal_Int32 spinCount; sal_Int32 spinCount;
sal_Int32 saltSize; sal_Int32 saltSize;
...@@ -39,15 +40,45 @@ struct AgileEncryptionInfo ...@@ -39,15 +40,45 @@ struct AgileEncryptionInfo
OUString hashAlgorithm; OUString hashAlgorithm;
std::vector<sal_uInt8> keyDataSalt; std::vector<sal_uInt8> keyDataSalt;
// Key Encryptor
std::vector<sal_uInt8> saltValue; std::vector<sal_uInt8> saltValue;
std::vector<sal_uInt8> encryptedVerifierHashInput; std::vector<sal_uInt8> encryptedVerifierHashInput;
std::vector<sal_uInt8> encryptedVerifierHashValue; std::vector<sal_uInt8> encryptedVerifierHashValue;
std::vector<sal_uInt8> encryptedKeyValue; std::vector<sal_uInt8> encryptedKeyValue;
// HMAC
std::vector<sal_uInt8> hmacKey;
std::vector<sal_uInt8> hmacHash;
std::vector<sal_uInt8> hmacCalculatedHash;
std::vector<sal_uInt8> hmacEncryptedKey; // encrypted Key
std::vector<sal_uInt8> hmacEncryptedValue; // encrypted Hash
};
struct OOX_DLLPUBLIC AgileEncryptionParameters
{
sal_Int32 spinCount;
sal_Int32 saltSize;
sal_Int32 keyBits;
sal_Int32 hashSize;
sal_Int32 blockSize;
OUString cipherAlgorithm;
OUString cipherChaining;
OUString hashAlgorithm;
}; };
class AgileEngine : public CryptoEngine enum class AgileEncryptionPreset
{ {
AES_128_SHA1,
AES_256_SHA512,
};
class OOX_DLLPUBLIC AgileEngine : public CryptoEngine
{
private:
AgileEncryptionInfo mInfo; AgileEncryptionInfo mInfo;
AgileEncryptionPreset meEncryptionPreset;
void calculateHashFinal(const OUString& rPassword, std::vector<sal_uInt8>& aHashFinal); void calculateHashFinal(const OUString& rPassword, std::vector<sal_uInt8>& aHashFinal);
...@@ -57,28 +88,59 @@ class AgileEngine : public CryptoEngine ...@@ -57,28 +88,59 @@ class AgileEngine : public CryptoEngine
std::vector<sal_uInt8>& rInput, std::vector<sal_uInt8>& rInput,
std::vector<sal_uInt8>& rOutput); std::vector<sal_uInt8>& rOutput);
void encryptBlock(
std::vector<sal_uInt8> const & rBlock,
std::vector<sal_uInt8>& rHashFinal,
std::vector<sal_uInt8>& rInput,
std::vector<sal_uInt8>& rOutput);
static Crypto::CryptoType cryptoType(const AgileEncryptionInfo& rInfo); static Crypto::CryptoType cryptoType(const AgileEncryptionInfo& rInfo);
bool calculateDecryptionKey(const OUString& rPassword);
public: public:
AgileEngine() = default; AgileEngine();
AgileEncryptionInfo& getInfo() { return mInfo;} AgileEncryptionInfo& getInfo() { return mInfo;}
virtual void writeEncryptionInfo( void setPreset(AgileEncryptionPreset ePreset)
const OUString& rPassword, {
BinaryXOutputStream& rStream) override; meEncryptionPreset = ePreset;
}
virtual bool generateEncryptionKey(const OUString& rPassword) override; // Decryption
virtual bool decrypt( bool decryptEncryptionKey(OUString const & rPassword);
BinaryXInputStream& aInputStream, bool decryptAndCheckVerifierHash(OUString const & rPassword);
BinaryXOutputStream& aOutputStream) override;
bool generateEncryptionKey(OUString const & rPassword) override;
bool readEncryptionInfo(css::uno::Reference<css::io::XInputStream> & rxInputStream) override; bool readEncryptionInfo(css::uno::Reference<css::io::XInputStream> & rxInputStream) override;
bool decrypt(BinaryXInputStream& aInputStream,
BinaryXOutputStream& aOutputStream) override;
bool checkDataIntegrity() override;
bool decryptHmacKey();
bool decryptHmacValue();
// Encryption
void writeEncryptionInfo(BinaryXOutputStream& rStream) override;
void encrypt(css::uno::Reference<css::io::XInputStream>& rxInputStream,
css::uno::Reference<css::io::XOutputStream>& rxOutputStream,
sal_uInt32 nSize) override;
bool setupEncryption(OUString const & rPassword) override;
bool generateAndEncryptVerifierHash(OUString const & rPassword);
bool encryptHmacKey();
bool encryptHmacValue();
virtual void encrypt( bool encryptEncryptionKey(OUString const & rPassword);
BinaryXInputStream& aInputStream, void setupEncryptionParameters(AgileEncryptionParameters const & rAgileEncryptionParameters);
BinaryXOutputStream& aOutputStream) override; bool setupEncryptionKey(OUString const & rPassword);
}; };
} // namespace core } // namespace core
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <sal/types.h> #include <sal/types.h>
#include <com/sun/star/io/XInputStream.hpp> #include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/XOutputStream.hpp>
namespace oox { namespace oox {
class BinaryXInputStream; class BinaryXInputStream;
...@@ -38,9 +39,8 @@ public: ...@@ -38,9 +39,8 @@ public:
virtual ~CryptoEngine() virtual ~CryptoEngine()
{} {}
virtual void writeEncryptionInfo( // Decryption
const OUString& rPassword, virtual bool readEncryptionInfo(css::uno::Reference<css::io::XInputStream> & rxInputStream) = 0;
BinaryXOutputStream& rStream) = 0;
virtual bool generateEncryptionKey(const OUString& rPassword) = 0; virtual bool generateEncryptionKey(const OUString& rPassword) = 0;
...@@ -48,11 +48,16 @@ public: ...@@ -48,11 +48,16 @@ public:
BinaryXInputStream& aInputStream, BinaryXInputStream& aInputStream,
BinaryXOutputStream& aOutputStream) = 0; BinaryXOutputStream& aOutputStream) = 0;
virtual bool readEncryptionInfo(css::uno::Reference<css::io::XInputStream> & rxInputStream) = 0; // Encryption
virtual void writeEncryptionInfo(BinaryXOutputStream & rStream) = 0;
virtual void encrypt( virtual bool setupEncryption(const OUString& rPassword) = 0;
BinaryXInputStream& aInputStream,
BinaryXOutputStream& aOutputStream) = 0; virtual void encrypt(css::uno::Reference<css::io::XInputStream> & rxInputStream,
css::uno::Reference<css::io::XOutputStream> & rxOutputStream,
sal_uInt32 nSize) = 0;
virtual bool checkDataIntegrity() = 0;
}; };
} // namespace core } // namespace core
......
...@@ -38,21 +38,23 @@ public: ...@@ -38,21 +38,23 @@ public:
msfilter::StandardEncryptionInfo& getInfo() { return mInfo;} msfilter::StandardEncryptionInfo& getInfo() { return mInfo;}
virtual bool generateEncryptionKey(const OUString& rPassword) override; bool readEncryptionInfo(css::uno::Reference<css::io::XInputStream> & rxInputStream) override;
virtual void writeEncryptionInfo( virtual bool generateEncryptionKey(OUString const & rPassword) override;
const OUString& rPassword,
BinaryXOutputStream& rStream) override;
virtual bool decrypt( virtual bool decrypt(
BinaryXInputStream& aInputStream, BinaryXInputStream& aInputStream,
BinaryXOutputStream& aOutputStream) override; BinaryXOutputStream& aOutputStream) override;
bool readEncryptionInfo(css::uno::Reference<css::io::XInputStream> & rxInputStream) override; bool checkDataIntegrity() override;
virtual void encrypt( void encrypt(css::uno::Reference<css::io::XInputStream>& rxInputStream,
BinaryXInputStream& aInputStream, css::uno::Reference<css::io::XOutputStream>& rxOutputStream,
BinaryXOutputStream& aOutputStream) override; sal_uInt32 nSize) override;
virtual void writeEncryptionInfo(BinaryXOutputStream& rStream) override;
virtual bool setupEncryption(OUString const & rPassword) override;
}; };
......
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
#include <tools/stream.hxx> #include <tools/stream.hxx>
#include <unotools/streamwrap.hxx> #include <unotools/streamwrap.hxx>
#include <oox/crypto/CryptTools.hxx>
#include <oox/crypto/Standard2007Engine.hxx> #include <oox/crypto/Standard2007Engine.hxx>
#include <oox/crypto/AgileEngine.hxx>
#include <oox/helper/binaryinputstream.hxx> #include <oox/helper/binaryinputstream.hxx>
#include <oox/helper/binaryoutputstream.hxx> #include <oox/helper/binaryoutputstream.hxx>
...@@ -28,11 +28,19 @@ public: ...@@ -28,11 +28,19 @@ public:
void testCryptoHash(); void testCryptoHash();
void testRoundUp(); void testRoundUp();
void testStandard2007(); void testStandard2007();
void testAgileEncryptionVerifier();
void testAgileEncrpytionInfoWritingAndParsing();
void testAgileDataIntegrityHmacKey();
void testAgileEncryptingAndDecrypting();
CPPUNIT_TEST_SUITE(CryptoTest); CPPUNIT_TEST_SUITE(CryptoTest);
CPPUNIT_TEST(testCryptoHash); CPPUNIT_TEST(testCryptoHash);
CPPUNIT_TEST(testRoundUp); CPPUNIT_TEST(testRoundUp);
CPPUNIT_TEST(testStandard2007); CPPUNIT_TEST(testStandard2007);
CPPUNIT_TEST(testAgileEncryptionVerifier);
CPPUNIT_TEST(testAgileEncrpytionInfoWritingAndParsing);
CPPUNIT_TEST(testAgileDataIntegrityHmacKey);
CPPUNIT_TEST(testAgileEncryptingAndDecrypting);
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
}; };
...@@ -100,11 +108,13 @@ void CryptoTest::testStandard2007() ...@@ -100,11 +108,13 @@ void CryptoTest::testStandard2007()
{ {
oox::core::Standard2007Engine aEngine; oox::core::Standard2007Engine aEngine;
{ {
aEngine.setupEncryption("Password");
SvMemoryStream aEncryptionInfo; SvMemoryStream aEncryptionInfo;
oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream( oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream(
new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), false); new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), false);
aEngine.writeEncryptionInfo("Password", aBinaryEncryptionInfoOutputStream); aEngine.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream);
aBinaryEncryptionInfoOutputStream.close(); aBinaryEncryptionInfoOutputStream.close();
CPPUNIT_ASSERT_EQUAL(sal_uInt64(224), aEncryptionInfo.GetSize()); CPPUNIT_ASSERT_EQUAL(sal_uInt64(224), aEncryptionInfo.GetSize());
...@@ -119,17 +129,14 @@ void CryptoTest::testStandard2007() ...@@ -119,17 +129,14 @@ void CryptoTest::testStandard2007()
aUnencryptedInput.Seek(STREAM_SEEK_TO_BEGIN); aUnencryptedInput.Seek(STREAM_SEEK_TO_BEGIN);
{ {
oox::BinaryXInputStream aBinaryInputStream( uno::Reference<io::XInputStream> xInputStream(
new utl::OSeekableInputStreamWrapper(aUnencryptedInput), true); new utl::OSeekableInputStreamWrapper(aUnencryptedInput));
oox::BinaryXOutputStream aBinaryOutputStream( uno::Reference<io::XOutputStream> xOutputStream(
new utl::OSeekableOutputStreamWrapper(aEncryptedStream), true); new utl::OSeekableOutputStreamWrapper(aEncryptedStream));
aEncryptedStream.WriteUInt32(aUnencryptedInput.GetSize()); aEngine.encrypt(xInputStream, xOutputStream, aUnencryptedInput.GetSize());
aEncryptedStream.WriteUInt32(0U);
aEngine.encrypt(aBinaryInputStream, aBinaryOutputStream); xOutputStream->flush();
aBinaryOutputStream.close();
aBinaryInputStream.close();
const sal_uInt8* pData = static_cast<const sal_uInt8*>(aEncryptedStream.GetData()); const sal_uInt8* pData = static_cast<const sal_uInt8*>(aEncryptedStream.GetData());
sal_uInt64 nSize = aEncryptedStream.GetSize(); sal_uInt64 nSize = aEncryptedStream.GetSize();
...@@ -164,6 +171,258 @@ void CryptoTest::testStandard2007() ...@@ -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_TEST_SUITE_REGISTRATION(CryptoTest);
CPPUNIT_PLUGIN_IMPLEMENT(); CPPUNIT_PLUGIN_IMPLEMENT();
......
...@@ -348,14 +348,21 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript ...@@ -348,14 +348,21 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript
{ {
// create temporary file for unencrypted package // create temporary file for unencrypted package
Reference<XStream> xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW ); Reference<XStream> xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW );
aDecryptor.decrypt( xTempFile );
// store temp file in media descriptor to keep it alive // if decryption was unsuccessful (corrupted file or any other reason)
rMediaDescriptor.setComponentDataEntry( "DecryptedPackage", Any( xTempFile ) ); if (!aDecryptor.decrypt(xTempFile))
{
Reference<XInputStream> xDecryptedInputStream = xTempFile->getInputStream(); rMediaDescriptor[ MediaDescriptor::PROP_ABORTED() ] <<= true;
if( lclIsZipPackage( mxContext, xDecryptedInputStream ) ) }
return xDecryptedInputStream; 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;
}
} }
} }
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <comphelper/sequence.hxx> #include <comphelper/sequence.hxx>
#include <filter/msfilter/mscodec.hxx> #include <filter/msfilter/mscodec.hxx>
#include <tools/XmlWriter.hxx>
#include <com/sun/star/io/XSeekable.hpp> #include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/io/XStream.hpp> #include <com/sun/star/io/XStream.hpp>
...@@ -146,6 +147,18 @@ public: ...@@ -146,6 +147,18 @@ public:
comphelper::Base64::decode(encryptedKeyValue, rAttribute.Value); comphelper::Base64::decode(encryptedKeyValue, rAttribute.Value);
mInfo.encryptedKeyValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedKeyValue); 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: ...@@ -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, bool hashCalc(std::vector<sal_uInt8>& output,
std::vector<sal_uInt8>& input, std::vector<sal_uInt8>& input,
...@@ -189,8 +208,19 @@ bool hashCalc(std::vector<sal_uInt8>& output, ...@@ -189,8 +208,19 @@ bool hashCalc(std::vector<sal_uInt8>& output,
return false; return false;
} }
CryptoHashType cryptoHashTypeFromString(OUString const & sAlgorithm)
{
if (sAlgorithm == "SHA512")
return CryptoHashType::SHA512;
return CryptoHashType::SHA1;
}
} // namespace } // namespace
AgileEngine::AgileEngine()
: meEncryptionPreset(AgileEncryptionPreset::AES_256_SHA512)
{}
Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo) Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo)
{ {
if (rInfo.keyBits == 128 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC") if (rInfo.keyBits == 128 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC")
...@@ -200,6 +230,19 @@ Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo) ...@@ -200,6 +230,19 @@ Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo)
return Crypto::UNKNOWN; 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( void AgileEngine::calculateBlock(
std::vector<sal_uInt8> const & rBlock, std::vector<sal_uInt8> const & rBlock,
std::vector<sal_uInt8>& rHashFinal, std::vector<sal_uInt8>& rHashFinal,
...@@ -222,21 +265,55 @@ void AgileEngine::calculateBlock( ...@@ -222,21 +265,55 @@ void AgileEngine::calculateBlock(
aDecryptor.update(rOutput, rInput); 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) void AgileEngine::calculateHashFinal(const OUString& rPassword, std::vector<sal_uInt8>& aHashFinal)
{ {
aHashFinal = comphelper::DocPasswordHelper::GetOoxHashAsVector( rPassword, mInfo.saltValue, aHashFinal = comphelper::DocPasswordHelper::GetOoxHashAsVector(
mInfo.spinCount, comphelper::Hash::IterCount::PREPEND, mInfo.hashAlgorithm); 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(); bool generateBytes(std::vector<sal_uInt8> & rBytes, sal_Int32 nSize)
mKey.resize(mInfo.keyBits / 8, 0); {
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); std::vector<sal_uInt8> hashFinal(mInfo.hashSize, 0);
calculateHashFinal(rPassword, hashFinal); calculateHashFinal(rPassword, hashFinal);
...@@ -251,22 +328,114 @@ bool AgileEngine::generateEncryptionKey(const OUString& rPassword) ...@@ -251,22 +328,114 @@ bool AgileEngine::generateEncryptionKey(const OUString& rPassword)
std::vector<sal_uInt8> hash(mInfo.hashSize, 0); std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
hashCalc(hash, hashInput, mInfo.hashAlgorithm); 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; decryptEncryptionKey(rPassword);
calculateBlock(constBlock3, hashFinal, encryptedKeyValue, mKey); decryptHmacKey();
return true; 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, bool AgileEngine::decrypt(BinaryXInputStream& aInputStream,
BinaryXOutputStream& aOutputStream) BinaryXOutputStream& aOutputStream)
{ {
CryptoHash aCryptoHash(mInfo.hmacKey, cryptoHashTypeFromString(mInfo.hashAlgorithm));
sal_uInt32 totalSize = aInputStream.readuInt32(); // Document unencrypted size - 4 bytes 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 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; std::vector<sal_uInt8>& keyDataSalt = mInfo.keyDataSalt;
sal_uInt32 saltSize = mInfo.saltSize; sal_uInt32 saltSize = mInfo.saltSize;
...@@ -286,7 +455,7 @@ bool AgileEngine::decrypt(BinaryXInputStream& aInputStream, ...@@ -286,7 +455,7 @@ bool AgileEngine::decrypt(BinaryXInputStream& aInputStream,
sal_uInt32 outputLength; sal_uInt32 outputLength;
sal_uInt32 remaining = totalSize; 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* segmentBegin = reinterpret_cast<sal_uInt8*>(&segment);
sal_uInt8* segmentEnd = segmentBegin + sizeof(segment); sal_uInt8* segmentEnd = segmentBegin + sizeof(segment);
...@@ -301,12 +470,17 @@ bool AgileEngine::decrypt(BinaryXInputStream& aInputStream, ...@@ -301,12 +470,17 @@ bool AgileEngine::decrypt(BinaryXInputStream& aInputStream,
outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength); outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength);
sal_uInt32 writeLength = std::min(outputLength, remaining); sal_uInt32 writeLength = std::min(outputLength, remaining);
aCryptoHash.update(inputBuffer, inputLength);
aOutputStream.writeMemory(outputBuffer.data(), writeLength); aOutputStream.writeMemory(outputBuffer.data(), writeLength);
remaining -= outputLength; remaining -= outputLength;
segment++; segment++;
} }
mInfo.hmacCalculatedHash = aCryptoHash.finalize();
return true; return true;
} }
...@@ -363,18 +537,279 @@ bool AgileEngine::readEncryptionInfo(uno::Reference<io::XInputStream> & rxInputS ...@@ -363,18 +537,279 @@ bool AgileEngine::readEncryptionInfo(uno::Reference<io::XInputStream> & rxInputS
return false; return false;
} }
void AgileEngine::writeEncryptionInfo( bool AgileEngine::generateAndEncryptVerifierHash(OUString const & rPassword)
const OUString& /*aPassword*/, {
BinaryXOutputStream& /*rStream*/) 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( void AgileEngine::writeEncryptionInfo(BinaryXOutputStream & rStream)
BinaryXInputStream& /*aInputStream*/,
BinaryXOutputStream& /*aOutputStream*/)
{ {
// 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 } // namespace core
......
...@@ -106,6 +106,9 @@ bool DocumentDecryption::decrypt(const uno::Reference<io::XStream>& xDocumentStr ...@@ -106,6 +106,9 @@ bool DocumentDecryption::decrypt(const uno::Reference<io::XStream>& xDocumentStr
xDecryptedPackage->flush(); xDecryptedPackage->flush();
aDecryptedPackage.seekToStart(); aDecryptedPackage.seekToStart();
if (bResult)
return mEngine->checkDataIntegrity();
return bResult; return bResult;
} }
......
...@@ -41,37 +41,30 @@ bool DocumentEncryption::encrypt() ...@@ -41,37 +41,30 @@ bool DocumentEncryption::encrypt()
if (!xSeekable.is()) if (!xSeekable.is())
return false; 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()) if (!mrOleStorage.isStorage())
return false; 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); Reference<XOutputStream> xEncryptionInfo(mrOleStorage.openOutputStream("EncryptionInfo"), UNO_SET_THROW);
BinaryXOutputStream aEncryptionInfoBinaryOutputStream(xEncryptionInfo, false); BinaryXOutputStream aEncryptionInfoBinaryOutputStream(xEncryptionInfo, false);
mEngine.writeEncryptionInfo(maPassword, aEncryptionInfoBinaryOutputStream); mEngine.writeEncryptionInfo(aEncryptionInfoBinaryOutputStream);
aEncryptionInfoBinaryOutputStream.close(); aEncryptionInfoBinaryOutputStream.close();
xEncryptionInfo->flush(); xEncryptionInfo->flush();
xEncryptionInfo->closeOutput(); 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; return true;
} }
......
...@@ -35,6 +35,7 @@ void lclRandomGenerateValues(sal_uInt8* aArray, sal_uInt32 aSize) ...@@ -35,6 +35,7 @@ void lclRandomGenerateValues(sal_uInt8* aArray, sal_uInt32 aSize)
} }
static const OUString lclCspName = "Microsoft Enhanced RSA and AES Cryptographic Provider"; static const OUString lclCspName = "Microsoft Enhanced RSA and AES Cryptographic Provider";
constexpr const sal_uInt32 AES128Size = 16;
} // end anonymous namespace } // end anonymous namespace
...@@ -172,7 +173,12 @@ bool Standard2007Engine::decrypt(BinaryXInputStream& aInputStream, ...@@ -172,7 +173,12 @@ bool Standard2007Engine::decrypt(BinaryXInputStream& aInputStream,
return true; 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.flags = msfilter::ENCRYPTINFO_AES | msfilter::ENCRYPTINFO_CRYPTOAPI;
mInfo.header.algId = msfilter::ENCRYPT_ALGO_AES128; mInfo.header.algId = msfilter::ENCRYPT_ALGO_AES128;
...@@ -187,11 +193,16 @@ void Standard2007Engine::writeEncryptionInfo(const OUString& password, BinaryXOu ...@@ -187,11 +193,16 @@ void Standard2007Engine::writeEncryptionInfo(const OUString& password, BinaryXOu
mKey.resize(keyLength, 0); mKey.resize(keyLength, 0);
if (!calculateEncryptionKey(password)) if (!calculateEncryptionKey(password))
return; return false;
if (!generateVerifier()) if (!generateVerifier())
return; return false;
return true;
}
void Standard2007Engine::writeEncryptionInfo(BinaryXOutputStream& rStream)
{
rStream.WriteUInt32(msfilter::VERSION_INFO_2007_FORMAT); rStream.WriteUInt32(msfilter::VERSION_INFO_2007_FORMAT);
sal_uInt32 cspNameSize = (lclCspName.getLength() * 2) + 2; sal_uInt32 cspNameSize = (lclCspName.getLength() * 2) + 2;
...@@ -209,9 +220,19 @@ void Standard2007Engine::writeEncryptionInfo(const OUString& password, BinaryXOu ...@@ -209,9 +220,19 @@ void Standard2007Engine::writeEncryptionInfo(const OUString& password, BinaryXOu
rStream.writeMemory(&mInfo.verifier, sizeof(msfilter::EncryptionVerifierAES)); rStream.writeMemory(&mInfo.verifier, sizeof(msfilter::EncryptionVerifierAES));
} }
void Standard2007Engine::encrypt(BinaryXInputStream& aInputStream, void Standard2007Engine::encrypt(css::uno::Reference<css::io::XInputStream> & rxInputStream,
BinaryXOutputStream& aOutputStream) 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> inputBuffer(1024);
std::vector<sal_uInt8> outputBuffer(1024); std::vector<sal_uInt8> outputBuffer(1024);
...@@ -221,11 +242,13 @@ void Standard2007Engine::encrypt(BinaryXInputStream& aInputStream, ...@@ -221,11 +242,13 @@ void Standard2007Engine::encrypt(BinaryXInputStream& aInputStream,
std::vector<sal_uInt8> iv; std::vector<sal_uInt8> iv;
Encrypt aEncryptor(mKey, iv, Crypto::AES_128_ECB); 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); outputLength = aEncryptor.update(outputBuffer, inputBuffer, inputLength);
aOutputStream.writeMemory(outputBuffer.data(), outputLength); aBinaryOutputStream.writeMemory(outputBuffer.data(), outputLength);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment