diff options
-rw-r--r-- | include/oox/core/DocumentCrypt.hxx | 148 | ||||
-rw-r--r-- | include/oox/crypto/AgileEngine.hxx | 81 | ||||
-rw-r--r-- | include/oox/crypto/CryptTools.hxx | 134 | ||||
-rw-r--r-- | include/oox/crypto/CryptoEngine.hxx | 59 | ||||
-rw-r--r-- | include/oox/crypto/DocumentDecryption.hxx | 72 | ||||
-rw-r--r-- | include/oox/crypto/DocumentEncryption.hxx | 55 | ||||
-rw-r--r-- | include/oox/crypto/Standard2007Engine.hxx | 118 | ||||
-rw-r--r-- | oox/Library_oox.mk | 6 | ||||
-rw-r--r-- | oox/source/core/DocumentCrypt.cxx | 610 | ||||
-rw-r--r-- | oox/source/core/filterdetect.cxx | 25 | ||||
-rw-r--r-- | oox/source/core/xmlfilterbase.cxx | 6 | ||||
-rw-r--r-- | oox/source/crypto/AgileEngine.cxx | 218 | ||||
-rw-r--r-- | oox/source/crypto/CryptTools.cxx | 255 | ||||
-rw-r--r-- | oox/source/crypto/DocumentDecryption.cxx | 400 | ||||
-rw-r--r-- | oox/source/crypto/DocumentEncryption.cxx | 79 | ||||
-rw-r--r-- | oox/source/crypto/Standard2007Engine.cxx | 288 |
16 files changed, 1780 insertions, 774 deletions
diff --git a/include/oox/core/DocumentCrypt.hxx b/include/oox/core/DocumentCrypt.hxx deleted file mode 100644 index 9831c190fdcf..000000000000 --- a/include/oox/core/DocumentCrypt.hxx +++ /dev/null @@ -1,148 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This file incorporates work covered by the following license notice: - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed - * with this work for additional information regarding copyright - * ownership. The ASF licenses this file to you under the Apache - * License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.apache.org/licenses/LICENSE-2.0 . - */ - -#ifndef DOCUMENTCRYPTO_HXX -#define DOCUMENTCRYPTO_HXX - -#include "oox/dllapi.h" - -#include "oox/ole/olestorage.hxx" -#include "oox/helper/binaryinputstream.hxx" -#include "oox/helper/binaryoutputstream.hxx" - -#include <com/sun/star/io/XStream.hpp> -#include <com/sun/star/beans/NamedValue.hpp> -#include <com/sun/star/uno/Sequence.hxx> - -#include <vector> - - -namespace oox { -namespace core { - -const sal_uInt32 ENCRYPTINFO_CRYPTOAPI = 0x00000004; -const sal_uInt32 ENCRYPTINFO_DOCPROPS = 0x00000008; -const sal_uInt32 ENCRYPTINFO_EXTERNAL = 0x00000010; -const sal_uInt32 ENCRYPTINFO_AES = 0x00000020; - -const sal_uInt32 ENCRYPT_ALGO_AES128 = 0x0000660E; -const sal_uInt32 ENCRYPT_ALGO_AES192 = 0x0000660F; -const sal_uInt32 ENCRYPT_ALGO_AES256 = 0x00006610; -const sal_uInt32 ENCRYPT_ALGO_RC4 = 0x00006801; - -const sal_uInt32 ENCRYPT_HASH_SHA1 = 0x00008004; - -const sal_uInt32 ENCRYPT_KEY_SIZE_AES_128 = 0x00000080; -const sal_uInt32 ENCRYPT_KEY_SIZE_AES_192 = 0x000000C0; -const sal_uInt32 ENCRYPT_KEY_SIZE_AES_256 = 0x00000100; - -const sal_uInt32 ENCRYPT_PROVIDER_TYPE_AES = 0x00000018; -const sal_uInt32 ENCRYPT_PROVIDER_TYPE_RC4 = 0x00000001; - -// version of encryption info used in MS Office 2007 (major = 3, minor = 2) -const sal_uInt32 VERSION_INFO_2007_FORMAT = 0x00030002; - -const sal_Int32 SALT_LENGTH = 16; -const sal_Int32 ENCRYPTED_VERIFIER_LENGTH = 16; -const sal_Int32 ENCRYPTED_VERIFIER_HASH_LENGTH = 32; - -struct EncryptionStandardHeader -{ - sal_uInt32 flags; - sal_uInt32 sizeExtra; // 0 - sal_uInt32 algId; // if flag AES && CRYPTOAPI this defaults to 128-bit AES - sal_uInt32 algIdHash; // 0: determine by flags - defaults to SHA-1 if not external - sal_uInt32 keySize; // key size in bits: 0 (determine by flags), 128, 192, 256 - sal_uInt32 providedType; // AES or RC4 - sal_uInt32 reserved1; // 0 - sal_uInt32 reserved2; // 0 - - EncryptionStandardHeader(); -}; - - -struct EncryptionVerifierAES -{ - sal_uInt32 saltSize; // must be 0x00000010 - sal_uInt8 salt[SALT_LENGTH]; // random generated salt value - sal_uInt8 encryptedVerifier[ENCRYPTED_VERIFIER_LENGTH]; // randomly generated verifier value - sal_uInt32 encryptedVerifierHashSize; // actually written hash size - depends on algorithm - sal_uInt8 encryptedVerifierHash[ENCRYPTED_VERIFIER_HASH_LENGTH]; // verifier value hash - itself also encrypted - - EncryptionVerifierAES(); -}; - -struct PackageEncryptionInfo -{ - EncryptionStandardHeader header; - EncryptionVerifierAES verifier; -}; - -class OOX_DLLPUBLIC AesEncoder -{ -private: - com::sun::star::uno::Reference< com::sun::star::io::XStream > mxDocumentStream; - oox::ole::OleStorage& mrOleStorage; - OUString maPassword; - - PackageEncryptionInfo mEncryptionInfo; - - bool checkEncryptionInfo(std::vector<sal_uInt8>& aKey, sal_uInt32 aKeyLength); - bool writeEncryptionInfo( BinaryOutputStream& rStream ); - -public: - AesEncoder( - com::sun::star::uno::Reference< com::sun::star::io::XStream > xDocumentStream, - oox::ole::OleStorage& rOleStorage, - OUString aPassword); - - bool encode(); - -}; - -class OOX_DLLPUBLIC AesDecoder -{ -private: - oox::ole::OleStorage& mrOleStorage; - PackageEncryptionInfo mEncryptionInfo; - std::vector<sal_uInt8> mKey; - sal_uInt32 mKeyLength; - - bool readEncryptionInfoFromStream( BinaryInputStream& rStream ); - -public: - AesDecoder(oox::ole::OleStorage& rOleStorage); - - bool decode(com::sun::star::uno::Reference< com::sun::star::io::XStream > xDocumentStream); - bool readEncryptionInfo(); - bool generateEncryptionKey(const OUString& rPassword); - - com::sun::star::uno::Sequence< com::sun::star::beans::NamedValue > createEncryptionData(); - - bool checkCurrentEncryptionData(); - - static bool checkEncryptionData( const com::sun::star::uno::Sequence< com::sun::star::beans::NamedValue >& rEncryptionData ); -}; - -} // namespace core -} // namespace oox - -#endif - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/oox/crypto/AgileEngine.hxx b/include/oox/crypto/AgileEngine.hxx new file mode 100644 index 000000000000..ddd7a3fffb50 --- /dev/null +++ b/include/oox/crypto/AgileEngine.hxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef AGILE_ENGINE_HXX +#define AGILE_ENGINE_HXX + +#include "CryptTools.hxx" +#include "CryptoEngine.hxx" + +namespace oox { +namespace core { + +const sal_uInt32 SEGMENT_LENGTH = 4096; + +struct AgileEncryptionInfo +{ + sal_Int32 spinCount; + sal_Int32 saltSize; + sal_Int32 keyBits; + sal_Int32 hashSize; + sal_Int32 blockSize; + + OUString cipherAlgorithm; + OUString cipherChaining; + OUString hashAlgorithm; + + std::vector<sal_uInt8> keyDataSalt; + std::vector<sal_uInt8> saltValue; + std::vector<sal_uInt8> encryptedVerifierHashInput; + std::vector<sal_uInt8> encryptedVerifierHashValue; + std::vector<sal_uInt8> encryptedKeyValue; +}; + +class AgileEngine : public CryptoEngine +{ + AgileEncryptionInfo mInfo; + + bool calculateHashFinal(const OUString& rPassword, std::vector<sal_uInt8>& aHashFinal); + + bool calculateBlock( + const std::vector<sal_uInt8>& rBlock, + std::vector<sal_uInt8>& rHashFinal, + std::vector<sal_uInt8>& rInput, + std::vector<sal_uInt8>& rOutput); + + Crypto::CryptoType cryptoType(const AgileEncryptionInfo& rInfo); + +public: + AgileEngine(); + virtual ~AgileEngine(); + + AgileEncryptionInfo& getInfo(); + + virtual bool writeEncryptionInfo( + const OUString& rPassword, + BinaryXOutputStream& rStream); + + virtual bool generateEncryptionKey(const OUString& rPassword); + + virtual bool decrypt( + BinaryXInputStream& aInputStream, + BinaryXOutputStream& aOutputStream); + + virtual bool encrypt( + BinaryXInputStream& aInputStream, + BinaryXOutputStream& aOutputStream); +}; + +} // namespace core +} // namespace oox + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/oox/crypto/CryptTools.hxx b/include/oox/crypto/CryptTools.hxx new file mode 100644 index 000000000000..3c9bf0ba0426 --- /dev/null +++ b/include/oox/crypto/CryptTools.hxx @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef CRYPT_TOOLS_HXX +#define CRYPT_TOOLS_HXX + +#include <config_oox.h> + +#include <rtl/ustring.hxx> + +#if USE_TLS_OPENSSL +#include <openssl/evp.h> +#include <openssl/sha.h> +#endif // USE_TLS_OPENSSL +#if USE_TLS_NSS +#include <nss.h> +#include <pk11pub.h> +#endif // USE_TLS_NSS + +#include <rtl/digest.h> +#include <vector> + +namespace oox { +namespace core { + +class Crypto +{ +public: + enum CryptoType + { + UNKNOWN, + AES_128_ECB, + AES_128_CBC, + AES_256_CBC, + }; + +protected: +#if USE_TLS_OPENSSL + EVP_CIPHER_CTX mContext; +#endif +#if USE_TLS_NSS + PK11Context* mContext; + SECItem* mSecParam; + PK11SymKey* mSymKey; +#endif + CryptoType mType; + +#if USE_TLS_OPENSSL + const EVP_CIPHER* getCipher(CryptoType type); +#endif +#if USE_TLS_NSS + void setupContext( + std::vector<sal_uInt8>& key, + std::vector<sal_uInt8>& iv, + CryptoType type, + CK_ATTRIBUTE_TYPE operation); +#endif + +public: + Crypto(CryptoType type); + + virtual ~Crypto(); + + virtual sal_uInt32 update( + std::vector<sal_uInt8>& output, + std::vector<sal_uInt8>& input, + sal_uInt32 inputLength = 0) = 0; +}; + +class Decrypt : public Crypto +{ +public: + Decrypt(std::vector<sal_uInt8>& key, CryptoType type); + Decrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type); + + virtual sal_uInt32 update( + std::vector<sal_uInt8>& output, + std::vector<sal_uInt8>& input, + sal_uInt32 inputLength = 0); + + + static sal_uInt32 aes128ecb( + std::vector<sal_uInt8>& output, + std::vector<sal_uInt8>& input, + std::vector<sal_uInt8>& key ); + + static sal_uInt32 aes128cbc( + std::vector<sal_uInt8>& output, + std::vector<sal_uInt8>& input, + std::vector<sal_uInt8>& key, + std::vector<sal_uInt8>& iv ); +}; + +class Encrypt : public Crypto +{ +public: + Encrypt(std::vector<sal_uInt8>& key, CryptoType type); + Encrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type); + + virtual sal_uInt32 update( + std::vector<sal_uInt8>& output, + std::vector<sal_uInt8>& input, + sal_uInt32 inputLength = 0); +}; + +const sal_uInt32 SHA1_LENGTH = 20; +const sal_uInt32 SHA512_LENGTH = 64; + +bool sha1( std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input ); + +bool sha512( std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input ); + +} // namespace core +} // namespace oox + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/oox/crypto/CryptoEngine.hxx b/include/oox/crypto/CryptoEngine.hxx new file mode 100644 index 000000000000..68bb0a8fa3f1 --- /dev/null +++ b/include/oox/crypto/CryptoEngine.hxx @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef CRYPTO_ENGINE_HXX +#define CRYPTO_ENGINE_HXX + +#include <vector> + +#include "oox/helper/binaryinputstream.hxx" +#include "oox/helper/binaryoutputstream.hxx" + +namespace oox { +namespace core { + +class CryptoEngine +{ +protected: + std::vector<sal_uInt8> mKey; + +public: + CryptoEngine() + {} + + virtual ~CryptoEngine() + {} + + virtual std::vector<sal_uInt8>& getKey() + { + return mKey; + } + + virtual bool writeEncryptionInfo( + const OUString& rPassword, + BinaryXOutputStream& rStream) = 0; + + virtual bool generateEncryptionKey(const OUString& rPassword) = 0; + + virtual bool decrypt( + BinaryXInputStream& aInputStream, + BinaryXOutputStream& aOutputStream) = 0; + + virtual bool encrypt( + BinaryXInputStream& aInputStream, + BinaryXOutputStream& aOutputStream) = 0; +}; + +} // namespace core +} // namespace oox + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/oox/crypto/DocumentDecryption.hxx b/include/oox/crypto/DocumentDecryption.hxx new file mode 100644 index 000000000000..768cdbf6b275 --- /dev/null +++ b/include/oox/crypto/DocumentDecryption.hxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef DOCUMENT_DECRYPTION_HXX +#define DOCUMENT_DECRYPTION_HXX + +#include "oox/dllapi.h" + +#include "oox/ole/olestorage.hxx" +#include "oox/helper/binaryinputstream.hxx" +#include "oox/helper/binaryoutputstream.hxx" + +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/uno/Sequence.hxx> + +#include "CryptTools.hxx" +#include "AgileEngine.hxx" +#include "Standard2007Engine.hxx" + +#include <boost/scoped_ptr.hpp> +#include <vector> + +namespace oox { +namespace core { + +class OOX_DLLPUBLIC DocumentDecryption +{ +private: + com::sun::star::uno::Reference< com::sun::star::uno::XComponentContext > mxContext; + + enum CryptoType + { + UNKNOWN, + STANDARD_2007, + AGILE + }; + + oox::ole::OleStorage& mrOleStorage; + boost::scoped_ptr<CryptoEngine> mEngine; + CryptoType mCryptoType; + + bool readAgileEncryptionInfo( com::sun::star::uno::Reference< com::sun::star::io::XInputStream >& rStream ); + bool readStandard2007EncryptionInfo( BinaryInputStream& rStream ); + +public: + DocumentDecryption( + oox::ole::OleStorage& rOleStorage, + com::sun::star::uno::Reference< com::sun::star::uno::XComponentContext > xContext); + + bool decrypt(com::sun::star::uno::Reference< com::sun::star::io::XStream > xDocumentStream); + bool readEncryptionInfo(); + bool generateEncryptionKey(const OUString& rPassword); + + com::sun::star::uno::Sequence< com::sun::star::beans::NamedValue > createEncryptionData(); + + static bool checkEncryptionData( const com::sun::star::uno::Sequence< com::sun::star::beans::NamedValue >& rEncryptionData ); +}; + +} // namespace core +} // namespace oox + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/oox/crypto/DocumentEncryption.hxx b/include/oox/crypto/DocumentEncryption.hxx new file mode 100644 index 000000000000..b4e142ea2ade --- /dev/null +++ b/include/oox/crypto/DocumentEncryption.hxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef DOCUMENT_ENCRYPTION_HXX +#define DOCUMENT_ENCRYPTION_HXX + +#include "oox/dllapi.h" + +#include "oox/ole/olestorage.hxx" + +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/uno/Sequence.hxx> + +#include "CryptTools.hxx" +#include "Standard2007Engine.hxx" + +#include <vector> + + +namespace oox { +namespace core { + +class OOX_DLLPUBLIC DocumentEncryption +{ +private: + com::sun::star::uno::Reference< com::sun::star::io::XStream > mxDocumentStream; + oox::ole::OleStorage& mrOleStorage; + OUString maPassword; + + Standard2007Engine mEngine; + +public: + DocumentEncryption( + com::sun::star::uno::Reference< com::sun::star::io::XStream > xDocumentStream, + oox::ole::OleStorage& rOleStorage, + OUString aPassword); + + bool encrypt(); + +}; + +} // namespace core +} // namespace oox + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/oox/crypto/Standard2007Engine.hxx b/include/oox/crypto/Standard2007Engine.hxx new file mode 100644 index 000000000000..c53ec22cd124 --- /dev/null +++ b/include/oox/crypto/Standard2007Engine.hxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#ifndef STANDARD_2007_ENGINE_HXX +#define STANDARD_2007_ENGINE_HXX + +#include "CryptTools.hxx" +#include "CryptoEngine.hxx" + +namespace oox { +namespace core { + +const sal_uInt32 ENCRYPTINFO_CRYPTOAPI = 0x00000004; +const sal_uInt32 ENCRYPTINFO_DOCPROPS = 0x00000008; +const sal_uInt32 ENCRYPTINFO_EXTERNAL = 0x00000010; +const sal_uInt32 ENCRYPTINFO_AES = 0x00000020; + +const sal_uInt32 ENCRYPT_ALGO_AES128 = 0x0000660E; +const sal_uInt32 ENCRYPT_ALGO_AES192 = 0x0000660F; +const sal_uInt32 ENCRYPT_ALGO_AES256 = 0x00006610; +const sal_uInt32 ENCRYPT_ALGO_RC4 = 0x00006801; + +const sal_uInt32 ENCRYPT_HASH_SHA1 = 0x00008004; + +const sal_uInt32 ENCRYPT_KEY_SIZE_AES_128 = 0x00000080; +const sal_uInt32 ENCRYPT_KEY_SIZE_AES_192 = 0x000000C0; +const sal_uInt32 ENCRYPT_KEY_SIZE_AES_256 = 0x00000100; + +const sal_uInt32 ENCRYPT_PROVIDER_TYPE_AES = 0x00000018; +const sal_uInt32 ENCRYPT_PROVIDER_TYPE_RC4 = 0x00000001; + +// version of encryption info used in MS Office 2007 (major = 3, minor = 2) +const sal_uInt32 VERSION_INFO_2007_FORMAT = 0x00020003; +// version of encryption info - agile (major = 4, minor = 4) +const sal_uInt32 VERSION_INFO_AGILE = 0x00040004; + +const sal_uInt32 SALT_LENGTH = 16; +const sal_uInt32 ENCRYPTED_VERIFIER_LENGTH = 16; +const sal_uInt32 ENCRYPTED_VERIFIER_HASH_LENGTH = 32; + +struct EncryptionStandardHeader +{ + sal_uInt32 flags; + sal_uInt32 sizeExtra; // 0 + sal_uInt32 algId; // if flag AES && CRYPTOAPI this defaults to 128-bit AES + sal_uInt32 algIdHash; // 0: determine by flags - defaults to SHA-1 if not external + sal_uInt32 keyBits; // key size in bits: 0 (determine by flags), 128, 192, 256 + sal_uInt32 providedType; // AES or RC4 + sal_uInt32 reserved1; // 0 + sal_uInt32 reserved2; // 0 + + EncryptionStandardHeader(); +}; + +struct EncryptionVerifierAES +{ + sal_uInt32 saltSize; // must be 0x00000010 + sal_uInt8 salt[SALT_LENGTH]; // random generated salt value + sal_uInt8 encryptedVerifier[ENCRYPTED_VERIFIER_LENGTH]; // randomly generated verifier value + sal_uInt32 encryptedVerifierHashSize; // actually written hash size - depends on algorithm + sal_uInt8 encryptedVerifierHash[ENCRYPTED_VERIFIER_HASH_LENGTH]; // verifier value hash - itself also encrypted + + EncryptionVerifierAES(); +}; + +struct StandardEncryptionInfo +{ + EncryptionStandardHeader header; + EncryptionVerifierAES verifier; +}; + +class Standard2007Engine : public CryptoEngine +{ + StandardEncryptionInfo mInfo; + + bool generateVerifier(); + bool calculateEncryptionKey(const OUString& rPassword); + +public: + Standard2007Engine(); + virtual ~Standard2007Engine(); + + StandardEncryptionInfo& getInfo(); + + static bool checkEncryptionData( + std::vector<sal_uInt8> key, sal_uInt32 keySize, + std::vector<sal_uInt8> encryptedVerifier, sal_uInt32 verifierSize, + std::vector<sal_uInt8> encryptedHash, sal_uInt32 hashSize ); + + virtual bool generateEncryptionKey(const OUString& rPassword); + + virtual bool writeEncryptionInfo( + const OUString& rPassword, + BinaryXOutputStream& rStream); + + virtual bool decrypt( + BinaryXInputStream& aInputStream, + BinaryXOutputStream& aOutputStream); + + virtual bool encrypt( + BinaryXInputStream& aInputStream, + BinaryXOutputStream& aOutputStream); + +}; + +} // namespace core +} // namespace oox + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/Library_oox.mk b/oox/Library_oox.mk index f8477369319b..a20b548c2e48 100644 --- a/oox/Library_oox.mk +++ b/oox/Library_oox.mk @@ -78,7 +78,6 @@ $(eval $(call gb_Library_add_exception_objects,oox,\ oox/source/core/binarycodec \ oox/source/core/contexthandler2 \ oox/source/core/contexthandler \ - oox/source/core/DocumentCrypt \ oox/source/core/fastparser \ oox/source/core/fasttokenhandler \ oox/source/core/filterbase \ @@ -90,6 +89,11 @@ $(eval $(call gb_Library_add_exception_objects,oox,\ oox/source/core/relationshandler \ oox/source/core/services \ oox/source/core/xmlfilterbase \ + oox/source/crypto/AgileEngine \ + oox/source/crypto/CryptTools \ + oox/source/crypto/DocumentEncryption \ + oox/source/crypto/DocumentDecryption \ + oox/source/crypto/Standard2007Engine \ oox/source/docprop/docprophandler \ oox/source/docprop/ooxmldocpropimport \ oox/source/drawingml/chart/axiscontext \ diff --git a/oox/source/core/DocumentCrypt.cxx b/oox/source/core/DocumentCrypt.cxx deleted file mode 100644 index ac0b255b1703..000000000000 --- a/oox/source/core/DocumentCrypt.cxx +++ /dev/null @@ -1,610 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - */ - -#include "oox/core/DocumentCrypt.hxx" -#include <config_oox.h> - -#if USE_TLS_OPENSSL -#include <openssl/evp.h> -#endif // USE_TLS_OPENSSL -#if USE_TLS_NSS -#include <nss.h> -#include <pk11pub.h> -#endif // USE_TLS_NSS -#include <rtl/digest.h> - -#include <comphelper/docpasswordhelper.hxx> -#include <comphelper/mediadescriptor.hxx> - -#include <osl/time.h> -#include <rtl/random.h> - -#include <com/sun/star/io/XSeekable.hpp> - -namespace oox { -namespace core { - -using namespace ::com::sun::star::beans; -using namespace ::com::sun::star::io; -using namespace ::com::sun::star::lang; -using namespace ::com::sun::star::uno; - -using ::comphelper::MediaDescriptor; -using ::comphelper::SequenceAsHashMap; - -using namespace std; - -/* =========================================================================== */ -/* Kudos to Caolan McNamara who provided the core decryption implementations. */ -/* =========================================================================== */ - -namespace { - -void lclRandomGenerateValues( sal_Int32 nLength, sal_uInt8* aArray ) -{ - TimeValue aTime; - osl_getSystemTime( &aTime ); - rtlRandomPool aRandomPool = rtl_random_createPool (); - rtl_random_addBytes ( aRandomPool, &aTime, 8 ); - rtl_random_getBytes ( aRandomPool, aArray, nLength ); - rtl_random_destroyPool ( aRandomPool ); -} - -void lclDeriveKey( const sal_uInt8* pnHash, sal_uInt32 nHashLen, vector<sal_uInt8>& rKey, sal_uInt32 aKeyLength ) -{ - // De facto we are always called with nRequiredKeyLen == 16, at least currently - assert(aKeyLength == 16); - - sal_uInt8 pnBuffer[ 64 ]; - memset( pnBuffer, 0x36, sizeof( pnBuffer ) ); - for( sal_uInt32 i = 0; i < nHashLen; ++i ) - pnBuffer[ i ] ^= pnHash[ i ]; - - rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 ); - rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) ); - sal_uInt8 pnX1[ RTL_DIGEST_LENGTH_SHA1 ]; - rtl_digest_get( aDigest, pnX1, RTL_DIGEST_LENGTH_SHA1 ); - rtl_digest_destroy( aDigest ); - - memset( pnBuffer, 0x5C, sizeof( pnBuffer ) ); - for( sal_uInt32 i = 0; i < nHashLen; ++i ) - pnBuffer[ i ] ^= pnHash[ i ]; - - aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 ); - rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) ); - sal_uInt8 pnX2[ RTL_DIGEST_LENGTH_SHA1 ]; - rtl_digest_get( aDigest, pnX2, RTL_DIGEST_LENGTH_SHA1 ); - rtl_digest_destroy( aDigest ); - -#if 0 // for now nRequiredKeyLen will always be 16 and thus less than - // RTL_DIGEST_LENGTH_SHA1==20, see assert above... - if( aKeyLength > RTL_DIGEST_LENGTH_SHA1 ) - { - // This memcpy call generates a (bogus?) warning when - // compiling with gcc 4.7 and 4.8 and optimising: array - // subscript is above array bounds. - std::copy(pnX1, pnX1 + aKeyLength - RTL_DIGEST_LENGTH_SHA1, rKey.begin() + RTL_DIGEST_LENGTH_SHA1); - aKeyLength = RTL_DIGEST_LENGTH_SHA1; - } -#endif - std::copy(pnX1, pnX1 + aKeyLength, rKey.begin()); - //memcpy( pnKeyDerived, pnX1, nRequiredKeyLen ); -} - -bool lclGenerateVerifier(PackageEncryptionInfo& rEncryptionInfo, const vector<sal_uInt8>& rKey, sal_uInt32 nKeySize) -{ - // only support key of size 128 bit (16 byte) - if (nKeySize != 16) - return false; - - sal_uInt8 aVerifier[ENCRYPTED_VERIFIER_LENGTH]; - sal_Int32 aVerifierSize = sizeof(aVerifier); - lclRandomGenerateValues(aVerifierSize, aVerifier); - -#if USE_TLS_OPENSSL - { - EVP_CIPHER_CTX aContext; - EVP_CIPHER_CTX_init( &aContext ); - EVP_EncryptInit_ex( &aContext, EVP_aes_128_ecb(), NULL, &rKey[0], 0 ); - EVP_CIPHER_CTX_set_padding( &aContext, 0 ); - int aWrittenLength = 0; - EVP_EncryptUpdate( &aContext, rEncryptionInfo.verifier.encryptedVerifier, &aWrittenLength, aVerifier, aVerifierSize ); - if (aWrittenLength != ENCRYPTED_VERIFIER_LENGTH) - return false; - EVP_CIPHER_CTX_cleanup( &aContext ); - } - -#endif // USE_TLS_OPENSSL - - sal_uInt8 pSha1Hash[ENCRYPTED_VERIFIER_HASH_LENGTH]; - memset(pSha1Hash, 0, sizeof(pSha1Hash)); - rEncryptionInfo.verifier.encryptedVerifierHashSize = RTL_DIGEST_LENGTH_SHA1; - - rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 ); - rtl_digest_update( aDigest, aVerifier, aVerifierSize ); - rtl_digest_get( aDigest, pSha1Hash, RTL_DIGEST_LENGTH_SHA1 ); - rtl_digest_destroy( aDigest ); - -#if USE_TLS_OPENSSL - { - int aWrittenLength = 0; - - EVP_CIPHER_CTX aContext; - EVP_CIPHER_CTX_init( &aContext ); - EVP_EncryptInit_ex( &aContext, EVP_aes_128_ecb(), NULL, &rKey[0], 0 ); - EVP_CIPHER_CTX_set_padding( &aContext, 0 ); - EVP_EncryptUpdate( &aContext, rEncryptionInfo.verifier.encryptedVerifierHash, &aWrittenLength, pSha1Hash, sizeof(pSha1Hash) ); - EVP_CIPHER_CTX_cleanup( &aContext ); - } -#endif // USE_TLS_OPENSSL - - return true; -} - -bool lclCheckEncryptionData( const sal_uInt8* pnKey, sal_uInt32 nKeySize, const sal_uInt8* pnVerifier, sal_uInt32 nVerifierSize, const sal_uInt8* pnVerifierHash, sal_uInt32 nVerifierHashSize ) -{ - // the only currently supported algorithm needs key size 128 - if ( nKeySize != 16 || nVerifierSize != 16 ) - return false; - - // check password -#if USE_TLS_OPENSSL - EVP_CIPHER_CTX aes_ctx; - EVP_CIPHER_CTX_init( &aes_ctx ); - EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, pnKey, 0 ); - EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 ); - int nOutLen = 0; - sal_uInt8 pnTmpVerifier[ 16 ]; - (void) memset( pnTmpVerifier, 0, sizeof(pnTmpVerifier) ); - - /*int*/ EVP_DecryptUpdate( &aes_ctx, pnTmpVerifier, &nOutLen, pnVerifier, nVerifierSize ); - EVP_CIPHER_CTX_cleanup( &aes_ctx ); - - EVP_CIPHER_CTX_init( &aes_ctx ); - EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, pnKey, 0 ); - EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 ); - sal_uInt8* pnTmpVerifierHash = new sal_uInt8[nVerifierHashSize]; - (void) memset( pnTmpVerifierHash, 0, nVerifierHashSize ); - - /*int*/ EVP_DecryptUpdate( &aes_ctx, pnTmpVerifierHash, &nOutLen, pnVerifierHash, nVerifierHashSize ); - EVP_CIPHER_CTX_cleanup( &aes_ctx ); -#endif // USE_TLS_OPENSSL - -#if USE_TLS_NSS - PK11SlotInfo *aSlot( PK11_GetBestSlot( CKM_AES_ECB, NULL ) ); - sal_uInt8 *key( new sal_uInt8[ nKeySize ] ); - (void) memcpy( key, pnKey, nKeySize * sizeof(sal_uInt8) ); - - SECItem keyItem; - keyItem.type = siBuffer; - keyItem.data = key; - keyItem.len = nKeySize; - - PK11SymKey *symKey( PK11_ImportSymKey( aSlot, CKM_AES_ECB, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, NULL ) ); - SECItem *secParam( PK11_ParamFromIV( CKM_AES_ECB, NULL ) ); - PK11Context *encContext( PK11_CreateContextBySymKey( CKM_AES_ECB, CKA_DECRYPT, symKey, secParam ) ); - - int nOutLen(0); - sal_uInt8 pnTmpVerifier[ 16 ]; - (void) memset( pnTmpVerifier, 0, sizeof(pnTmpVerifier) ); - - PK11_CipherOp( encContext, pnTmpVerifier, &nOutLen, sizeof(pnTmpVerifier), const_cast<sal_uInt8*>(pnVerifier), nVerifierSize ); - - sal_uInt8* pnTmpVerifierHash = new sal_uInt8[nVerifierHashSize]; - (void) memset( pnTmpVerifierHash, 0, nVerifierHashSize ); - PK11_CipherOp( encContext, pnTmpVerifierHash, &nOutLen, nVerifierHashSize, const_cast<sal_uInt8*>(pnVerifierHash), nVerifierHashSize ); - - PK11_DestroyContext( encContext, PR_TRUE ); - PK11_FreeSymKey( symKey ); - SECITEM_FreeItem( secParam, PR_TRUE ); - delete[] key; -#endif // USE_TLS_NSS - - rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 ); - rtl_digest_update( aDigest, pnTmpVerifier, sizeof( pnTmpVerifier ) ); - sal_uInt8 pnSha1Hash[ RTL_DIGEST_LENGTH_SHA1 ]; - rtl_digest_get( aDigest, pnSha1Hash, RTL_DIGEST_LENGTH_SHA1 ); - rtl_digest_destroy( aDigest ); - - return memcmp( pnSha1Hash, pnTmpVerifierHash, RTL_DIGEST_LENGTH_SHA1 ) == 0; -} - -bool lclGenerateEncryptionKey( const PackageEncryptionInfo& rEncryptionInfo, const OUString& rPassword, vector<sal_uInt8>& aKey, sal_uInt32 aKeyLength ) -{ - size_t nBufferSize = rEncryptionInfo.verifier.saltSize + 2 * rPassword.getLength(); - sal_uInt8* pnBuffer = new sal_uInt8[ nBufferSize ]; - memcpy( pnBuffer, rEncryptionInfo.verifier.salt, rEncryptionInfo.verifier.saltSize ); - - sal_uInt8* pnPasswordLoc = pnBuffer + rEncryptionInfo.verifier.saltSize; - const sal_Unicode* pStr = rPassword.getStr(); - for( sal_Int32 i = 0, nLen = rPassword.getLength(); i < nLen; ++i, ++pStr, pnPasswordLoc += 2 ) - ByteOrderConverter::writeLittleEndian( pnPasswordLoc, static_cast< sal_uInt16 >( *pStr ) ); - - rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 ); - rtl_digest_update( aDigest, pnBuffer, nBufferSize ); - delete[] pnBuffer; - - size_t nHashSize = RTL_DIGEST_LENGTH_SHA1 + 4; - sal_uInt8* pnHash = new sal_uInt8[ nHashSize ]; - rtl_digest_get( aDigest, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 ); - rtl_digest_destroy( aDigest ); - - for( sal_uInt32 i = 0; i < 50000; ++i ) - { - ByteOrderConverter::writeLittleEndian( pnHash, i ); - aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 ); - rtl_digest_update( aDigest, pnHash, nHashSize ); - rtl_digest_get( aDigest, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 ); - rtl_digest_destroy( aDigest ); - } - - memmove( pnHash, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 ); - memset( pnHash + RTL_DIGEST_LENGTH_SHA1, 0, 4 ); - aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 ); - rtl_digest_update( aDigest, pnHash, nHashSize ); - rtl_digest_get( aDigest, pnHash, RTL_DIGEST_LENGTH_SHA1 ); - rtl_digest_destroy( aDigest ); - - lclDeriveKey( pnHash, RTL_DIGEST_LENGTH_SHA1, aKey, aKeyLength ); - delete[] pnHash; - return true; -} - -} // namespace - -EncryptionStandardHeader::EncryptionStandardHeader() -{ - flags = 0; - sizeExtra = 0; - algId = 0; - algIdHash = 0; - keySize = 0; - providedType = 0; - reserved1 = 0; - reserved2 = 0; -} - -EncryptionVerifierAES::EncryptionVerifierAES() -{ - saltSize = SALT_LENGTH; - memset(salt, 0, sizeof(salt)); - memset(encryptedVerifier, 0, sizeof(encryptedVerifier)); - memset(encryptedVerifierHash, 0, sizeof(encryptedVerifierHash)); -} - -AesEncoder::AesEncoder(Reference< XStream > xDocumentStream, oox::ole::OleStorage& rOleStorage, OUString aPassword) : - mxDocumentStream(xDocumentStream), - mrOleStorage(rOleStorage), - maPassword(aPassword) -{ -} - -bool AesEncoder::checkEncryptionInfo(vector<sal_uInt8>& aKey, sal_uInt32 aKeyLength) -{ - return lclCheckEncryptionData( - &aKey[0], aKeyLength, - mEncryptionInfo.verifier.encryptedVerifier, sizeof(mEncryptionInfo.verifier.encryptedVerifier), - mEncryptionInfo.verifier.encryptedVerifierHash, ENCRYPTED_VERIFIER_HASH_LENGTH); -} - -bool AesEncoder::writeEncryptionInfo( BinaryOutputStream& rStream ) -{ - rStream.writeValue(VERSION_INFO_2007_FORMAT); - - const OUString cspName = "Microsoft Enhanced RSA and AES Cryptographic Provider"; - sal_Int32 cspNameSize = (cspName.getLength() * 2) + 2; - - sal_Int32 encryptionHeaderSize = static_cast<sal_Int32>(sizeof(EncryptionStandardHeader)); - - rStream << mEncryptionInfo.header.flags; - sal_uInt32 headerSize = encryptionHeaderSize + cspNameSize; - rStream << headerSize; - - rStream.writeMemory(&mEncryptionInfo.header, encryptionHeaderSize); - rStream.writeUnicodeArray(cspName); - rStream.writeValue<sal_uInt16>(0); - - sal_Int32 encryptionVerifierSize = static_cast<sal_Int32>(sizeof(EncryptionVerifierAES)); - rStream.writeMemory(&mEncryptionInfo.verifier, encryptionVerifierSize); - - return true; -} - -bool AesEncoder::encode() -{ - Reference< XInputStream > xInputStream ( mxDocumentStream->getInputStream(), UNO_SET_THROW ); - Reference< XSeekable > xSeekable( xInputStream, UNO_QUERY ); - - if (!xSeekable.is()) - return false; - - sal_uInt32 aLength = xSeekable->getLength(); - - if (!mrOleStorage.isStorage()) - return false; - - Reference< XOutputStream > xEncryptionInfo( mrOleStorage.openOutputStream( "EncryptionInfo" ), UNO_SET_THROW ); - - mEncryptionInfo.header.flags = ENCRYPTINFO_AES | ENCRYPTINFO_CRYPTOAPI; - mEncryptionInfo.header.algId = ENCRYPT_ALGO_AES128; - mEncryptionInfo.header.algIdHash = ENCRYPT_HASH_SHA1; - mEncryptionInfo.header.keySize = ENCRYPT_KEY_SIZE_AES_128; - mEncryptionInfo.header.providedType = ENCRYPT_PROVIDER_TYPE_AES; - - lclRandomGenerateValues( mEncryptionInfo.verifier.saltSize, mEncryptionInfo.verifier.salt ); - - const sal_Int32 keyLength = mEncryptionInfo.header.keySize / 8; - vector<sal_uInt8> aKey; - aKey.resize(keyLength, 0); - - assert(keyLength == 16); - - lclGenerateEncryptionKey(mEncryptionInfo, maPassword, aKey, keyLength); - - sal_uInt8 key[16]; - std::copy(aKey.begin(), aKey.end(), key); - - lclGenerateVerifier(mEncryptionInfo, aKey, keyLength); - - if (!checkEncryptionInfo(aKey, keyLength)) - return false; - - BinaryXOutputStream aEncryptionInfoBinaryOutputStream( xEncryptionInfo, false ); - 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(); - -#if USE_TLS_OPENSSL - EVP_CIPHER_CTX aContext; - EVP_CIPHER_CTX_init( &aContext ); - EVP_EncryptInit_ex( &aContext, EVP_aes_128_ecb(), NULL, key, 0 ); - EVP_CIPHER_CTX_set_padding( &aContext, 0 ); - - sal_uInt8 inBuffer[ 1024 ]; - sal_uInt8 outBuffer[ 1024 ]; - - sal_Int32 inLength; - int outLength; - - aEncryptedPackageStream.writeValue<sal_uInt32>( aLength ); // size - aEncryptedPackageStream.writeValue<sal_uInt32>( 0 ); // size - - do - { - inLength = aDocumentInputStream.readMemory( inBuffer, sizeof( inBuffer ) ); - if (inLength > 0) - { - inLength = inLength % 16 == 0 ? inLength : ((inLength/16)*16)+16; - EVP_EncryptUpdate( &aContext, outBuffer, &outLength, inBuffer, inLength ); - aEncryptedPackageStream.writeMemory( outBuffer, outLength ); - } - } - while (inLength > 0); - - EVP_CIPHER_CTX_cleanup( &aContext ); - -#endif // USE_TLS_OPENSSL - - aEncryptedPackageStream.seekToStart(); - aEncryptedPackageStream.close(); - - aDocumentInputStream.seekToStart(); - aDocumentInputStream.close(); - - xEncryptedPackage->flush(); - xEncryptedPackage->closeOutput(); - - return true; -} - -bool AesDecoder::checkCurrentEncryptionData() -{ - return lclCheckEncryptionData( - &mKey[0], mKeyLength, - mEncryptionInfo.verifier.encryptedVerifier, ENCRYPTED_VERIFIER_LENGTH, - mEncryptionInfo.verifier.encryptedVerifierHash, ENCRYPTED_VERIFIER_HASH_LENGTH ); -} - -bool AesDecoder::checkEncryptionData(const Sequence<NamedValue>& rEncryptionData) -{ - SequenceAsHashMap aHashData( rEncryptionData ); - Sequence<sal_Int8> aKey = aHashData.getUnpackedValueOrDefault( "AES128EncryptionKey", Sequence<sal_Int8>() ); - Sequence<sal_Int8> aVerifier = aHashData.getUnpackedValueOrDefault( "AES128EncryptionVerifier", Sequence<sal_Int8>() ); - Sequence<sal_Int8> aVerifierHash = aHashData.getUnpackedValueOrDefault( "AES128EncryptionVerifierHash", Sequence<sal_Int8>() ); - - return lclCheckEncryptionData( - reinterpret_cast<const sal_uInt8*>( aKey.getConstArray() ), aKey.getLength(), - reinterpret_cast<const sal_uInt8*>( aVerifier.getConstArray() ), aVerifier.getLength(), - reinterpret_cast<const sal_uInt8*>( aVerifierHash.getConstArray() ), aVerifierHash.getLength() ); -} - -bool AesDecoder::generateEncryptionKey(const OUString& rPassword) -{ - return lclGenerateEncryptionKey(mEncryptionInfo, rPassword, mKey, mKeyLength); -} - -AesDecoder::AesDecoder(oox::ole::OleStorage& rOleStorage) : - mrOleStorage(rOleStorage), - mKeyLength(0) -{ -#if USE_TLS_NSS - // Initialize NSS, database functions are not needed - NSS_NoDB_Init( NULL ); -#endif // USE_TLS_NSS -} - -bool AesDecoder::readEncryptionInfoFromStream( BinaryInputStream& rStream ) -{ - sal_uInt32 aVersion; - rStream >> aVersion; - - if (aVersion != VERSION_INFO_2007_FORMAT) - return false; - - rStream >> mEncryptionInfo.header.flags; - if( getFlag( mEncryptionInfo.header.flags, ENCRYPTINFO_EXTERNAL ) ) - return false; - - sal_uInt32 nHeaderSize; - rStream >> nHeaderSize; - - sal_uInt32 actualHeaderSize = sizeof(mEncryptionInfo.header); - - if( (nHeaderSize < actualHeaderSize) ) - return false; - - rStream >> mEncryptionInfo.header; - rStream.skip( nHeaderSize - actualHeaderSize ); - rStream >> mEncryptionInfo.verifier; - - if( mEncryptionInfo.verifier.saltSize != 16 ) - return false; - return !rStream.isEof(); -} - -bool AesDecoder::readEncryptionInfo() -{ - if( !mrOleStorage.isStorage() ) - return false; - - Reference< XInputStream > xEncryptionInfo( mrOleStorage.openInputStream( "EncryptionInfo" ), UNO_SET_THROW ); - - // read the encryption info stream - BinaryXInputStream aInputStream( xEncryptionInfo, true ); - bool bValidInfo = readEncryptionInfoFromStream( aInputStream ); - - if (!bValidInfo) - return false; - - // check flags and algorithm IDs, required are AES128 and SHA-1 - bool bImplemented = - getFlag( mEncryptionInfo.header.flags , ENCRYPTINFO_CRYPTOAPI ) && - getFlag( mEncryptionInfo.header.flags, ENCRYPTINFO_AES ) && - // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set - ((mEncryptionInfo.header.algId == 0) || (mEncryptionInfo.header.algId == ENCRYPT_ALGO_AES128)) && - // hash algorithm ID 0 defaults to SHA-1 too - ((mEncryptionInfo.header.algIdHash == 0) || (mEncryptionInfo.header.algIdHash == ENCRYPT_HASH_SHA1)) && - (mEncryptionInfo.verifier.encryptedVerifierHashSize == 20); - - mKeyLength = (mEncryptionInfo.header.keySize / 8); - mKey.clear(); - mKey.resize(mKeyLength, 0); - - return bImplemented; -} - -Sequence<NamedValue> AesDecoder::createEncryptionData() -{ - Sequence<NamedValue> aResult; - - if (mKeyLength > 0) - { - SequenceAsHashMap aEncryptionData; - EncryptionVerifierAES& verifier = mEncryptionInfo.verifier; - aEncryptionData["AES128EncryptionKey"] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( &mKey[0] ), mKeyLength ); - aEncryptionData["AES128EncryptionSalt"] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( verifier.salt ), verifier.saltSize ); - aEncryptionData["AES128EncryptionVerifier"] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( verifier.encryptedVerifier ), sizeof( verifier.encryptedVerifier ) ); - aEncryptionData["AES128EncryptionVerifierHash"] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( verifier.encryptedVerifierHash ), sizeof( verifier.encryptedVerifierHash ) ); - aResult = aEncryptionData.getAsConstNamedValueList(); - } - - return aResult; -} - -bool AesDecoder::decode( com::sun::star::uno::Reference< com::sun::star::io::XStream > xDocumentStream ) -{ - if( !mrOleStorage.isStorage() ) - return false; - - // open the required input streams in the encrypted package - Reference< XInputStream > xEncryptedPackage( mrOleStorage.openInputStream( "EncryptedPackage" ), UNO_SET_THROW ); - - // create temporary file for unencrypted package - Reference< XOutputStream > xDecryptedPackage( xDocumentStream->getOutputStream(), UNO_SET_THROW ); - BinaryXOutputStream aDecryptedPackage( xDecryptedPackage, true ); - BinaryXInputStream aEncryptedPackage( xEncryptedPackage, true ); - -#if USE_TLS_OPENSSL - EVP_CIPHER_CTX aes_ctx; - EVP_CIPHER_CTX_init( &aes_ctx ); - EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, &mKey.front(), 0 ); - EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 ); -#endif // USE_TLS_OPENSSL - -#if USE_TLS_NSS - PK11SlotInfo* aSlot( PK11_GetBestSlot( CKM_AES_ECB, NULL ) ); - sal_uInt8* key = new sal_uInt8[ mKeyLength ]; - std::copy(mKey.begin(), mKey.end(), key); - - SECItem keyItem; - keyItem.type = siBuffer; - keyItem.data = key; - keyItem.len = mKeyLength; - - PK11SymKey* symKey( PK11_ImportSymKey( aSlot, CKM_AES_ECB, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, NULL ) ); - SECItem* secParam( PK11_ParamFromIV( CKM_AES_ECB, NULL ) ); - PK11Context* encContext( PK11_CreateContextBySymKey( CKM_AES_ECB, CKA_DECRYPT, symKey, secParam ) ); -#endif // USE_TLS_NSS - - sal_uInt8 pnInBuffer[ 1024 ]; - sal_uInt8 pnOutBuffer[ 1024 ]; - sal_Int32 nInLen; - int nOutLen; - aEncryptedPackage.skip( 8 ); // decrypted size - while( (nInLen = aEncryptedPackage.readMemory( pnInBuffer, sizeof( pnInBuffer ) )) > 0 ) - { -#if USE_TLS_OPENSSL - EVP_DecryptUpdate( &aes_ctx, pnOutBuffer, &nOutLen, pnInBuffer, nInLen ); -#endif // USE_TLS_OPENSSL - -#if USE_TLS_NSS - PK11_CipherOp( encContext, pnOutBuffer, &nOutLen, sizeof(pnOutBuffer), pnInBuffer, nInLen ); -#endif // USE_TLS_NSS - aDecryptedPackage.writeMemory( pnOutBuffer, nOutLen ); - } -#if USE_TLS_OPENSSL - EVP_DecryptFinal_ex( &aes_ctx, pnOutBuffer, &nOutLen ); -#endif // USE_TLS_OPENSSL - -#if USE_TLS_NSS - uint finalLength; - PK11_DigestFinal( encContext, pnOutBuffer, &finalLength, nInLen - nOutLen ); - nOutLen = finalLength; -#endif // USE_TLS_NSS - aDecryptedPackage.writeMemory( pnOutBuffer, nOutLen ); - -#if USE_TLS_OPENSSL - EVP_CIPHER_CTX_cleanup( &aes_ctx ); -#endif // USE_TLS_OPENSSL - -#if USE_TLS_NSS - PK11_DestroyContext( encContext, PR_TRUE ); - PK11_FreeSymKey( symKey ); - SECITEM_FreeItem( secParam, PR_TRUE ); - delete[] key; -#endif // USE_TLS_NSS - xDecryptedPackage->flush(); - aDecryptedPackage.seekToStart(); - - return true; -} - -} // namespace core -} // namespace oox - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/core/filterdetect.cxx b/oox/source/core/filterdetect.cxx index 232864211bbe..7dd3f9210003 100644 --- a/oox/source/core/filterdetect.cxx +++ b/oox/source/core/filterdetect.cxx @@ -24,12 +24,13 @@ #include <comphelper/docpasswordhelper.hxx> #include <comphelper/mediadescriptor.hxx> -#include "oox/core/DocumentCrypt.hxx" #include "oox/core/fastparser.hxx" #include "oox/helper/attributelist.hxx" #include "oox/helper/zipstorage.hxx" #include "oox/ole/olestorage.hxx" +#include "oox/crypto/DocumentDecryption.hxx" + #include <com/sun/star/uri/UriReferenceFactory.hpp> namespace oox { @@ -270,23 +271,23 @@ bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Ref class PasswordVerifier : public IDocPasswordVerifier { public: - explicit PasswordVerifier( AesDecoder& decoder ); + explicit PasswordVerifier( DocumentDecryption& aDecryptor ); virtual DocPasswordVerifierResult verifyPassword( const OUString& rPassword, Sequence<NamedValue>& rEncryptionData ); virtual DocPasswordVerifierResult verifyEncryptionData( const Sequence<NamedValue>& rEncryptionData ); private: - AesDecoder& mDecoder; + DocumentDecryption& mDecryptor; }; -PasswordVerifier::PasswordVerifier( AesDecoder& decoder ) : - mDecoder(decoder) +PasswordVerifier::PasswordVerifier( DocumentDecryption& aDecryptor ) : + mDecryptor(aDecryptor) {} comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OUString& rPassword, Sequence<NamedValue>& rEncryptionData ) { - if( mDecoder.generateEncryptionKey(rPassword) && mDecoder.checkCurrentEncryptionData() ) - rEncryptionData = mDecoder.createEncryptionData(); + if( mDecryptor.generateEncryptionKey(rPassword) ) + rEncryptionData = mDecryptor.createEncryptionData(); return rEncryptionData.hasElements() ? comphelper::DocPasswordVerifierResult_OK : comphelper::DocPasswordVerifierResult_WRONG_PASSWORD; } @@ -294,7 +295,7 @@ comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OU comphelper::DocPasswordVerifierResult PasswordVerifier::verifyEncryptionData( const Sequence<NamedValue>& rEncryptionData ) { comphelper::DocPasswordVerifierResult aResult = comphelper::DocPasswordVerifierResult_WRONG_PASSWORD; - if (AesDecoder::checkEncryptionData(rEncryptionData)) + if (DocumentDecryption::checkEncryptionData(rEncryptionData)) aResult = comphelper::DocPasswordVerifierResult_OK; return aResult; } @@ -324,9 +325,9 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript { try { - AesDecoder aDecoder(aOleStorage); + DocumentDecryption aDecryptor(aOleStorage, mxContext); - if( aDecoder.readEncryptionInfo() ) + if( aDecryptor.readEncryptionInfo() ) { /* "VelvetSweatshop" is the built-in default encryption password used by MS Excel for the "workbook protection" @@ -339,7 +340,7 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript This helper returns either with the correct password (according to the verifier), or with an empty string if user has cancelled the password input dialog. */ - PasswordVerifier aVerifier( aDecoder ); + PasswordVerifier aVerifier( aDecryptor ); Sequence<NamedValue> aEncryptionData; aEncryptionData = comphelper::DocPasswordHelper::requestAndVerifyDocPassword( aVerifier, rMediaDescriptor, @@ -354,7 +355,7 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript { // create temporary file for unencrypted package Reference<XStream> xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW ); - aDecoder.decode( xTempFile ); + aDecryptor.decrypt( xTempFile ); // store temp file in media descriptor to keep it alive rMediaDescriptor.setComponentDataEntry( "DecryptedPackage", Any( xTempFile ) ); diff --git a/oox/source/core/xmlfilterbase.cxx b/oox/source/core/xmlfilterbase.cxx index 0b723a4a3c69..763090ab2b20 100644 --- a/oox/source/core/xmlfilterbase.cxx +++ b/oox/source/core/xmlfilterbase.cxx @@ -49,7 +49,7 @@ #include <oox/core/filterdetect.hxx> #include <comphelper/storagehelper.hxx> -#include <oox/core/DocumentCrypt.hxx> +#include <oox/crypto/DocumentEncryption.hxx> using ::com::sun::star::xml::dom::DocumentBuilder; using ::com::sun::star::xml::dom::XDocument; @@ -704,8 +704,8 @@ bool XmlFilterBase::implFinalizeExport( MediaDescriptor& rMediaDescriptor ) Reference< XStream> xDocumentStream (FilterBase::implGetOutputStream(rMediaDescriptor)); oox::ole::OleStorage aOleStorage( getComponentContext(), xDocumentStream, true ); - AesEncoder encoder(getMainDocumentStream(), aOleStorage, aPassword); - bRet = encoder.encode(); + DocumentEncryption encryptor(getMainDocumentStream(), aOleStorage, aPassword); + bRet = encryptor.encrypt(); if (bRet) aOleStorage.commit(); } diff --git a/oox/source/crypto/AgileEngine.cxx b/oox/source/crypto/AgileEngine.cxx new file mode 100644 index 000000000000..d0bebe9c1a7d --- /dev/null +++ b/oox/source/crypto/AgileEngine.cxx @@ -0,0 +1,218 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include "oox/crypto/AgileEngine.hxx" + +namespace oox { +namespace core { + +using namespace std; + +namespace { + +static const vector<sal_uInt8> vectorBlock1({ 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79 }); +static const vector<sal_uInt8> vectorBlock2({ 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e }); +static const vector<sal_uInt8> vectorBlock3({ 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6 }); + +bool hashCalc( std::vector<sal_uInt8>& output, + std::vector<sal_uInt8>& input, + const OUString& algorithm ) +{ + if (algorithm == "SHA1") + return sha1(output, input); + else if (algorithm == "SHA512") + return sha512(output, input); + return false; +} + +} // namespace + +AgileEngine::AgileEngine() : + CryptoEngine() +{} + +AgileEngine::~AgileEngine() +{} + +AgileEncryptionInfo& AgileEngine::getInfo() +{ + return mInfo; +} + +Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo) +{ + if (rInfo.keyBits == 128 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC") + return Crypto::AES_128_CBC; + else if (rInfo.keyBits == 256 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC") + return Crypto::AES_256_CBC; + return Crypto::UNKNOWN; +} + +bool AgileEngine::calculateBlock( + const vector<sal_uInt8>& rBlock, + vector<sal_uInt8>& rHashFinal, + vector<sal_uInt8>& rInput, + vector<sal_uInt8>& rOutput) +{ + vector<sal_uInt8> hash(mInfo.hashSize, 0); + vector<sal_uInt8> salt = mInfo.saltValue; + vector<sal_uInt8> dataFinal(mInfo.hashSize + rBlock.size(), 0); + std::copy(rHashFinal.begin(), rHashFinal.end(), dataFinal.begin()); + std::copy( + rBlock.begin(), + rBlock.begin() + rBlock.size(), + dataFinal.begin() + mInfo.hashSize); + + hashCalc(hash, dataFinal, mInfo.hashAlgorithm); + + sal_Int32 keySize = mInfo.keyBits / 8; + vector<sal_uInt8> key(keySize, 0); + + std::copy(hash.begin(), hash.begin() + keySize, key.begin()); + + Decrypt aDecryptor(key, salt, cryptoType(mInfo)); + aDecryptor.update(rOutput, rInput); + + return true; +} + +bool AgileEngine::calculateHashFinal(const OUString& rPassword, vector<sal_uInt8>& aHashFinal) +{ + sal_Int32 saltSize = mInfo.saltSize; + vector<sal_uInt8> salt = mInfo.saltValue; + sal_uInt32 passwordByteLength = rPassword.getLength() * 2; + + vector<sal_uInt8> initialData(saltSize + passwordByteLength); + std::copy(salt.begin(), salt.end(), initialData.begin()); + + const sal_uInt8* passwordByteArray = reinterpret_cast<const sal_uInt8*>(rPassword.getStr()); + + std::copy( + passwordByteArray, + passwordByteArray + passwordByteLength, + initialData.begin() + saltSize); + + vector<sal_uInt8> hash(mInfo.hashSize, 0); + + hashCalc(hash, initialData, mInfo.hashAlgorithm); + + vector<sal_uInt8> data(mInfo.hashSize + 4, 0); + + for (int i = 0; i < mInfo.spinCount; i++) + { + ByteOrderConverter::writeLittleEndian( &data[0], i ); + std::copy(hash.begin(), hash.end(), data.begin() + 4); + hashCalc(hash, data, mInfo.hashAlgorithm); + } + + std::copy(hash.begin(), hash.end(), aHashFinal.begin()); + + return true; +} + +bool AgileEngine::generateEncryptionKey(const OUString& rPassword) +{ + mKey.clear(); + mKey.resize(mInfo.keyBits / 8, 0); + + vector<sal_uInt8> hashFinal(mInfo.hashSize, 0); + calculateHashFinal(rPassword, hashFinal); + + vector<sal_uInt8> encryptedHashInput = mInfo.encryptedVerifierHashInput; + vector<sal_uInt8> hashInput(mInfo.saltSize, 0); + calculateBlock(vectorBlock1, hashFinal, encryptedHashInput, hashInput); + + vector<sal_uInt8> encryptedHashValue = mInfo.encryptedVerifierHashValue; + vector<sal_uInt8> hashValue(encryptedHashValue.size(), 0); + calculateBlock(vectorBlock2, hashFinal, encryptedHashValue, hashValue); + + vector<sal_uInt8> hash(mInfo.hashSize, 0); + hashCalc(hash, hashInput, mInfo.hashAlgorithm); + + if (std::equal (hash.begin(), hash.end(), hashValue.begin()) ) + { + vector<sal_uInt8> encryptedKeyValue = mInfo.encryptedKeyValue; + calculateBlock(vectorBlock3, hashFinal, encryptedKeyValue, mKey); + return true; + } + + return false; +} + +bool AgileEngine::decrypt( + BinaryXInputStream& aInputStream, + BinaryXOutputStream& aOutputStream) +{ + sal_uInt32 totalSize; + aInputStream >> totalSize; // Document unencrypted size - 4 bytes + aInputStream.skip( 4 ); // Reserved 4 Bytes + + vector<sal_uInt8> keyDataSalt = mInfo.keyDataSalt; + + sal_uInt32 saltSize = mInfo.saltSize; + sal_uInt32 keySize = mInfo.keyBits / 8; + + sal_uInt32 segment = 0; + + vector<sal_uInt8> saltWithBlockKey(saltSize + sizeof(segment), 0); + std::copy(keyDataSalt.begin(), keyDataSalt.end(), saltWithBlockKey.begin()); + + vector<sal_uInt8> hash(mInfo.hashSize, 0); + vector<sal_uInt8> iv(keySize, 0); + + vector<sal_uInt8> inputBuffer (SEGMENT_LENGTH); + vector<sal_uInt8> outputBuffer(SEGMENT_LENGTH); + sal_uInt32 inputLength; + sal_uInt32 outputLength; + sal_uInt32 remaining = totalSize; + + while( (inputLength = aInputStream.readMemory( &inputBuffer[0], SEGMENT_LENGTH )) > 0 ) + { + sal_uInt8* segmentBegin = reinterpret_cast<sal_uInt8*>(&segment); + sal_uInt8* segmentEnd = segmentBegin + sizeof(segment); + 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()); + + Decrypt aDecryptor(mKey, iv, AgileEngine::cryptoType(mInfo)); + outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength); + + sal_uInt32 writeLength = outputLength > remaining ? remaining : outputLength; + aOutputStream.writeMemory( &outputBuffer[0], writeLength ); + + remaining -= outputLength; + segment++; + } + + return true; +} + +bool AgileEngine::writeEncryptionInfo( + const OUString& /*aPassword*/, + BinaryXOutputStream& /*rStream*/) +{ + return false; // Agile encrypting is not supported for now +} + +bool AgileEngine::encrypt( + BinaryXInputStream& /*aInputStream*/, + BinaryXOutputStream& /*aOutputStream*/) +{ + return false; // Agile encrypting is not supported for now +} + + +} // namespace core +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/crypto/CryptTools.cxx b/oox/source/crypto/CryptTools.cxx new file mode 100644 index 000000000000..a2a75733aade --- /dev/null +++ b/oox/source/crypto/CryptTools.cxx @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include "oox/crypto/CryptTools.hxx" + +namespace oox { +namespace core { + +using namespace std; + +Crypto::Crypto(CryptoType type) : + mType(type) +{ +#if USE_TLS_NSS + // Initialize NSS, database functions are not needed + NSS_NoDB_Init(NULL); +#endif // USE_TLS_NSS +} + +Crypto::~Crypto() +{ +#if USE_TLS_OPENSSL + EVP_CIPHER_CTX_cleanup( &mContext ); +#endif +#if USE_TLS_NSS + PK11_DestroyContext( mContext, PR_TRUE ); + PK11_FreeSymKey( mSymKey ); + SECITEM_FreeItem( mSecParam, PR_TRUE ); +#endif +} + +#if USE_TLS_OPENSSL +const EVP_CIPHER* Crypto::getCipher(CryptoType type) +{ + switch(type) + { + case AES_128_ECB: + return EVP_aes_128_ecb(); + case AES_128_CBC: + return EVP_aes_128_cbc(); + case AES_256_CBC: + return EVP_aes_256_cbc(); + default: + break; + } + return NULL; +} +#endif + +#if USE_TLS_NSS +void Crypto::setupContext(vector<sal_uInt8>& key, vector<sal_uInt8>& iv, CryptoType type, CK_ATTRIBUTE_TYPE operation) +{ + CK_MECHANISM_TYPE mechanism = -1; + + SECItem ivItem; + ivItem.type = siBuffer; + ivItem.data = &iv[0]; + ivItem.len = iv.size(); + + SECItem* pIvItem = NULL; + + switch(type) + { + case AES_128_ECB: + mechanism = CKM_AES_ECB; + break; + case AES_128_CBC: + mechanism = CKM_AES_CBC; + pIvItem = &ivItem; + break; + case AES_256_CBC: + mechanism = CKM_AES_CBC; + pIvItem = &ivItem; + break; + default: + break; + } + + PK11SlotInfo* aSlot( PK11_GetBestSlot( mechanism, NULL ) ); + + SECItem keyItem; + keyItem.type = siBuffer; + keyItem.data = &key[0]; + keyItem.len = key.size(); + + mSymKey = PK11_ImportSymKey( aSlot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, NULL ); + mSecParam = PK11_ParamFromIV( mechanism, pIvItem ); + mContext = PK11_CreateContextBySymKey( mechanism, operation, mSymKey, mSecParam ); +} +#endif // USE_TLS_NSS + +// DECRYPT + +Decrypt::Decrypt(vector<sal_uInt8>& key, vector<sal_uInt8>& iv, CryptoType type) : + Crypto(type) +{ +#if USE_TLS_OPENSSL + EVP_CIPHER_CTX_init( &mContext ); + + const EVP_CIPHER* cipher = getCipher(type); + + if (iv.empty()) + EVP_DecryptInit_ex( &mContext, cipher, NULL, &key[0], 0 ); + else + EVP_DecryptInit_ex( &mContext, cipher, NULL, &key[0], &iv[0] ); + EVP_CIPHER_CTX_set_padding( &mContext, 0 ); +#endif + +#if USE_TLS_NSS + setupContext(key, iv, type, CKA_DECRYPT); +#endif // USE_TLS_NSS +} + +sal_uInt32 Decrypt::update(vector<sal_uInt8>& output, vector<sal_uInt8>& input, sal_uInt32 inputLength) +{ + int outputLength = 0; + + sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength; + +#if USE_TLS_OPENSSL + EVP_DecryptUpdate( &mContext, &output[0], &outputLength, &input[0], actualInputLength ); +#endif // USE_TLS_OPENSSL + +#if USE_TLS_NSS + PK11_CipherOp( mContext, &output[0], &outputLength, actualInputLength, &input[0], actualInputLength ); +#endif // USE_TLS_NSS + + return static_cast<sal_uInt32>(outputLength); +} + +sal_uInt32 Decrypt::aes128ecb(vector<sal_uInt8>& output, vector<sal_uInt8>& input, vector<sal_uInt8>& key) +{ + sal_uInt32 outputLength = 0; + vector<sal_uInt8> iv; + Decrypt crypto(key, iv, Crypto::AES_128_ECB); + outputLength = crypto.update(output, input); + return outputLength; +} + +sal_uInt32 Decrypt::aes128cbc(vector<sal_uInt8>& output, vector<sal_uInt8>& input, vector<sal_uInt8>& key, vector<sal_uInt8>& iv) +{ + sal_uInt32 outputLength = 0; + Decrypt crypto(key, iv, Crypto::AES_128_CBC); + outputLength = crypto.update(output, input); + return outputLength; +} + +// ENCRYPT + +Encrypt::Encrypt(vector<sal_uInt8>& key, vector<sal_uInt8>& iv, CryptoType type) : + Crypto(type) +{ +#if USE_TLS_OPENSSL + EVP_CIPHER_CTX_init( &mContext ); + + const EVP_CIPHER* cipher = getCipher(type); + + if (iv.empty()) + EVP_EncryptInit_ex( &mContext, cipher, NULL, &key[0], 0 ); + else + EVP_EncryptInit_ex( &mContext, cipher, NULL, &key[0], &iv[0] ); + EVP_CIPHER_CTX_set_padding( &mContext, 0 ); +#endif + +#if USE_TLS_NSS + setupContext(key, iv, type, CKA_ENCRYPT); +#endif // USE_TLS_NSS +} + +sal_uInt32 Encrypt::update(vector<sal_uInt8>& output, vector<sal_uInt8>& input, sal_uInt32 inputLength) +{ + int outputLength = 0; + + sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength; + +#if USE_TLS_OPENSSL + EVP_EncryptUpdate( &mContext, &output[0], &outputLength, &input[0], actualInputLength ); +#endif // USE_TLS_OPENSSL + +#if USE_TLS_NSS + PK11_CipherOp( mContext, &output[0], &outputLength, actualInputLength, &input[0], actualInputLength ); +#endif // USE_TLS_NSS + + return static_cast<sal_uInt32>(outputLength); +} + +bool sha1(vector<sal_uInt8>& output, vector<sal_uInt8>& input) +{ + output.clear(); + output.resize(RTL_DIGEST_LENGTH_SHA1, 0); + + rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 ); + rtl_digest_update( aDigest, &input[0], input.size() ); + rtl_digest_get( aDigest, &output[0], RTL_DIGEST_LENGTH_SHA1 ); + rtl_digest_destroy( aDigest ); + + return true; +} + +bool sha512(vector<sal_uInt8>& output, vector<sal_uInt8>& input) +{ + bool aResult = false; + +#if USE_TLS_OPENSSL + output.clear(); + output.resize(SHA512_DIGEST_LENGTH, 0); + + SHA512_CTX context; + SHA512_Init(&context); + SHA512_Update(&context, &input[0], input.size()); + SHA512_Final(&output[0], &context); + aResult = true; +#endif + +#if USE_TLS_NSS + output.clear(); + output.resize(SHA512_LENGTH, 0); + + // Initialize NSS, database functions are not needed + NSS_NoDB_Init(NULL); + SECStatus status; + + PK11Context* mContext = PK11_CreateDigestContext(SEC_OID_SHA512); + status = PK11_DigestBegin(mContext); + if (status != SECSuccess) + return false; + + status = PK11_DigestOp(mContext, &input[0], input.size()); + if (status != SECSuccess) + return false; + + sal_uInt32 outputLength = 0; + + status = PK11_DigestFinal(mContext, &output[0], &outputLength, SHA512_LENGTH); + if (status != SECSuccess || outputLength != SHA512_LENGTH) + return false; + + PK11_DestroyContext(mContext, PR_TRUE); + + aResult = true; +#endif + return aResult; +} + +} // namespace core +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/crypto/DocumentDecryption.cxx b/oox/source/crypto/DocumentDecryption.cxx new file mode 100644 index 000000000000..64281981ab29 --- /dev/null +++ b/oox/source/crypto/DocumentDecryption.cxx @@ -0,0 +1,400 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include "oox/crypto/DocumentDecryption.hxx" + +#include <comphelper/sequenceashashmap.hxx> +#include <sax/tools/converter.hxx> +#include <cppuhelper/implbase1.hxx> + +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/xml/sax/XFastParser.hpp> +#include <com/sun/star/xml/sax/XFastTokenHandler.hpp> +#include <com/sun/star/xml/sax/FastToken.hpp> + +namespace oox { +namespace core { + +using namespace css::beans; +using namespace css::io; +using namespace css::lang; +using namespace css::uno; +using namespace css::xml::sax; +using namespace css::xml; + +using namespace std; + +using ::comphelper::SequenceAsHashMap; +using ::sax::Converter; + +namespace { + +vector<sal_uInt8> convertToVector(Sequence<sal_Int8>& input) +{ + const sal_uInt8* inputArray = reinterpret_cast<const sal_uInt8*>( input.getConstArray() ); + return vector<sal_uInt8>(inputArray, inputArray + input.getLength()); +} + +class AgileTokenHandler : public cppu::WeakImplHelper1< XFastTokenHandler > +{ +public: + virtual sal_Int32 SAL_CALL getToken( const OUString& /*nIdentifier*/ ) throw (RuntimeException) + { + return FastToken::DONTKNOW; + } + + virtual sal_Int32 SAL_CALL getTokenFromUTF8( const Sequence< sal_Int8 >& /*nIdentifier*/ ) throw (RuntimeException) + { + return FastToken::DONTKNOW; + } + + virtual OUString SAL_CALL getIdentifier( sal_Int32 /*nToken*/ ) throw (RuntimeException) + { + return OUString(); + } + + virtual Sequence<sal_Int8> SAL_CALL getUTF8Identifier(sal_Int32 /*nToken*/) throw (RuntimeException) + { + return Sequence<sal_Int8>(); + } +}; + +class AgileDocumentHandler : public ::cppu::WeakImplHelper1< XFastDocumentHandler > +{ +public: + AgileDocumentHandler(AgileEncryptionInfo& rInfo) : + mInfo(rInfo) + {} + + AgileEncryptionInfo& mInfo; + void startDocument() {} + void endDocument() {} + void SAL_CALL setDocumentLocator( const Reference< XLocator >& /*xLocator*/ ) {} + void startFastElement( sal_Int32 /*Element*/, const Reference< XFastAttributeList >& /*Attribs*/ ) {} + + void startUnknownElement( const OUString& /*aNamespace*/, const OUString& aName, const Reference< XFastAttributeList >& aAttributeList ) + { + if(aName == "keyData") + { + Sequence<Attribute> aAttributes(aAttributeList->getUnknownAttributes()); + + for (int i=0; i<aAttributes.getLength(); i++) + { + if (aAttributes[i].Name == "saltValue") + { + Sequence<sal_Int8> keyDataSalt; + Converter::decodeBase64(keyDataSalt, aAttributes[i].Value); + mInfo.keyDataSalt = convertToVector(keyDataSalt); + } + } + } + else if(aName == "encryptedKey") + { + Sequence<Attribute> aAttributes(aAttributeList->getUnknownAttributes()); + for (int i=0; i<aAttributes.getLength(); i++) + { + if (aAttributes[i].Name == "spinCount") + { + Converter::convertNumber(mInfo.spinCount, aAttributes[i].Value); + } + else if (aAttributes[i].Name == "saltSize") + { + Converter::convertNumber(mInfo.saltSize, aAttributes[i].Value); + } + else if (aAttributes[i].Name == "blockSize") + { + Converter::convertNumber(mInfo.blockSize, aAttributes[i].Value); + } + else if (aAttributes[i].Name == "keyBits") + { + Converter::convertNumber(mInfo.keyBits, aAttributes[i].Value); + } + else if (aAttributes[i].Name == "hashSize") + { + Converter::convertNumber(mInfo.hashSize, aAttributes[i].Value); + } + else if (aAttributes[i].Name == "cipherAlgorithm") + { + mInfo.cipherAlgorithm = aAttributes[i].Value; + } + else if (aAttributes[i].Name == "cipherChaining") + { + mInfo.cipherChaining = aAttributes[i].Value; + } + else if (aAttributes[i].Name == "hashAlgorithm") + { + mInfo.hashAlgorithm = aAttributes[i].Value; + } + else if (aAttributes[i].Name == "saltValue") + { + Sequence<sal_Int8> saltValue; + Converter::decodeBase64(saltValue, aAttributes[i].Value); + mInfo.saltValue = convertToVector(saltValue); + } + else if (aAttributes[i].Name == "encryptedVerifierHashInput") + { + Sequence<sal_Int8> encryptedVerifierHashInput; + Converter::decodeBase64(encryptedVerifierHashInput, aAttributes[i].Value); + mInfo.encryptedVerifierHashInput = convertToVector(encryptedVerifierHashInput); + } + else if (aAttributes[i].Name == "encryptedVerifierHashValue") + { + Sequence<sal_Int8> encryptedVerifierHashValue; + Converter::decodeBase64(encryptedVerifierHashValue, aAttributes[i].Value); + mInfo.encryptedVerifierHashValue = convertToVector(encryptedVerifierHashValue); + } + else if (aAttributes[i].Name == "encryptedKeyValue") + { + Sequence<sal_Int8> encryptedKeyValue; + Converter::decodeBase64(encryptedKeyValue, aAttributes[i].Value); + mInfo.encryptedKeyValue = convertToVector(encryptedKeyValue); + } + } + } + } + + void endFastElement( sal_Int32 /*aElement*/ ) {} + void endUnknownElement( const OUString& /*aNamespace*/, const OUString& /*aName*/ ) {} + + Reference< XFastContextHandler > createFastChildContext( sal_Int32 /*aElement*/, const Reference< XFastAttributeList >& /*aAttribs*/ ) + { + return NULL; + } + + Reference< XFastContextHandler > createUnknownChildContext( const OUString& /*aNamespace*/, const OUString& /*aName*/, const Reference< XFastAttributeList >& /*aAttribs*/ ) + { + return this; + } + + void characters( const OUString& /*aChars*/ ) {} +}; + +} // namespace + +DocumentDecryption::DocumentDecryption(oox::ole::OleStorage& rOleStorage, Reference<XComponentContext> xContext) : + mxContext(xContext), + mrOleStorage(rOleStorage), + mCryptoType(UNKNOWN) +{} + +bool DocumentDecryption::checkEncryptionData(const Sequence<NamedValue>& rEncryptionData) +{ + SequenceAsHashMap aHashData( rEncryptionData ); + OUString type = aHashData.getUnpackedValueOrDefault( "CryptoType", OUString("Unknown") ); + if (type == "Standard") + { + Sequence<sal_Int8> aKeySeq = aHashData.getUnpackedValueOrDefault( "AES128EncryptionKey", Sequence<sal_Int8>() ); + Sequence<sal_Int8> aVerifierSeq = aHashData.getUnpackedValueOrDefault( "AES128EncryptionVerifier", Sequence<sal_Int8>() ); + Sequence<sal_Int8> aHashSeq = aHashData.getUnpackedValueOrDefault( "AES128EncryptionVerifierHash", Sequence<sal_Int8>() ); + + vector<sal_uInt8> key = convertToVector(aKeySeq); + vector<sal_uInt8> verifier = convertToVector(aVerifierSeq); + vector<sal_uInt8> hash = convertToVector(aHashSeq); + + return Standard2007Engine::checkEncryptionData( key, key.size(), verifier, verifier.size(), hash, hash.size() ); + } + return type == "Agile"; +} + +bool DocumentDecryption::generateEncryptionKey(const OUString& rPassword) +{ + if (mEngine.get()) + return mEngine->generateEncryptionKey(rPassword); + return false; +} + +bool DocumentDecryption::readAgileEncryptionInfo(Reference< XInputStream >& xInputStream) +{ + AgileEngine* engine = new AgileEngine(); + mEngine.reset(engine); + AgileEncryptionInfo& info = engine->getInfo(); + + Reference<XMultiComponentFactory> xFactory( mxContext->getServiceManager(), UNO_SET_THROW ); + Reference<XFastDocumentHandler> xFastDocumentHandler( new AgileDocumentHandler(info) ); + Reference<XFastTokenHandler> xFastTokenHandler ( new AgileTokenHandler ); + + Reference<XFastParser> xParser; + xParser.set( xFactory->createInstanceWithContext( "com.sun.star.xml.sax.FastParser", mxContext ), UNO_QUERY_THROW ); + + if (!xParser.is()) + return false; + + xParser->setFastDocumentHandler( xFastDocumentHandler ); + xParser->setTokenHandler( xFastTokenHandler ); + + InputSource aInputSource; + aInputSource.aInputStream = xInputStream; + xParser->parseStream( aInputSource ); + + // CHECK info data + if (2 > info.blockSize || info.blockSize > 4096) + return false; + + if (0 > info.spinCount || info.spinCount > 10000000) + return false; + + if (1 > info.saltSize|| info.saltSize > 65536) // Check + return false; + + // AES 128 CBC with SHA1 + if (info.keyBits == 128 && + info.cipherAlgorithm == "AES" && + info.cipherChaining == "ChainingModeCBC" && + info.hashAlgorithm == "SHA1" && + info.hashSize == 20) + { + return true; + } + + // AES 256 CBC with SHA512 + if (info.keyBits == 256 && + info.cipherAlgorithm == "AES" && + info.cipherChaining == "ChainingModeCBC" && + info.hashAlgorithm == "SHA512" && + info.hashSize == 64 ) + { + return true; + } + + return false; +} + +bool DocumentDecryption::readStandard2007EncryptionInfo(BinaryInputStream& rStream) +{ + Standard2007Engine* engine = new Standard2007Engine(); + mEngine.reset(engine); + StandardEncryptionInfo& info = engine->getInfo(); + + + rStream >> info.header.flags; + if( getFlag( info.header.flags, ENCRYPTINFO_EXTERNAL ) ) + return false; + + sal_uInt32 nHeaderSize; + rStream >> nHeaderSize; + + sal_uInt32 actualHeaderSize = sizeof(info.header); + + if( (nHeaderSize < actualHeaderSize) ) + return false; + + rStream >> info.header; + rStream.skip( nHeaderSize - actualHeaderSize ); + rStream >> info.verifier; + + if( info.verifier.saltSize != 16 ) + return false; + + // check flags and algorithm IDs, required are AES128 and SHA-1 + if( !getFlag( info.header.flags , ENCRYPTINFO_CRYPTOAPI ) ) + return false; + + if( !getFlag( info.header.flags, ENCRYPTINFO_AES ) ) + return false; + + // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set + if( info.header.algId != 0 && info.header.algId != ENCRYPT_ALGO_AES128 ) + return false; + + // hash algorithm ID 0 defaults to SHA-1 too + if( info.header.algIdHash != 0 && info.header.algIdHash != ENCRYPT_HASH_SHA1 ) + return false; + + if( info.verifier.encryptedVerifierHashSize != 20 ) + return false; + + return !rStream.isEof(); +} + +bool DocumentDecryption::readEncryptionInfo() +{ + if( !mrOleStorage.isStorage() ) + return false; + + Reference< XInputStream > xEncryptionInfo( mrOleStorage.openInputStream( "EncryptionInfo" ), UNO_SET_THROW ); + + bool bResult = false; + + BinaryXInputStream aBinaryInputStream( xEncryptionInfo, true ); + + sal_uInt32 aVersion; + aBinaryInputStream >> aVersion; + + switch (aVersion) + { + case VERSION_INFO_2007_FORMAT: + mCryptoType = STANDARD_2007; // Set encryption info format + bResult = readStandard2007EncryptionInfo( aBinaryInputStream ); + break; + case VERSION_INFO_AGILE: + mCryptoType = AGILE; // Set encryption info format + aBinaryInputStream.skip(4); + bResult = readAgileEncryptionInfo( xEncryptionInfo ); + break; + default: + break; + } + + return bResult; +} + +Sequence<NamedValue> DocumentDecryption::createEncryptionData() +{ + Sequence<NamedValue> aResult; + + vector<sal_uInt8>& key = mEngine->getKey(); + + if (key.size() > 0) + { + SequenceAsHashMap aEncryptionData; + if (mCryptoType == AGILE) + { + aEncryptionData["CryptoType"] <<= OUString("Agile"); + aEncryptionData["AES128EncryptionKey"] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( &key[0] ), key.size() ); + aResult = aEncryptionData.getAsConstNamedValueList(); + } + else if (mCryptoType == STANDARD_2007) + { + aEncryptionData["CryptoType"] <<= OUString("Standard"); + aEncryptionData["AES128EncryptionKey"] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( &key[0] ), key.size() ); + aResult = aEncryptionData.getAsConstNamedValueList(); + } + } + + return aResult; +} + +bool DocumentDecryption::decrypt(Reference<XStream> xDocumentStream) +{ + bool aResult = false; + + if( !mrOleStorage.isStorage() ) + return false; + + // open the required input streams in the encrypted package + Reference< XInputStream > xEncryptedPackage( mrOleStorage.openInputStream( "EncryptedPackage" ), UNO_SET_THROW ); + + // create temporary file for unencrypted package + Reference< XOutputStream > xDecryptedPackage( xDocumentStream->getOutputStream(), UNO_SET_THROW ); + BinaryXOutputStream aDecryptedPackage( xDecryptedPackage, true ); + BinaryXInputStream aEncryptedPackage( xEncryptedPackage, true ); + + aResult = mEngine->decrypt(aEncryptedPackage, aDecryptedPackage); + + xDecryptedPackage->flush(); + aDecryptedPackage.seekToStart(); + + return aResult; +} + +} // namespace core +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/crypto/DocumentEncryption.cxx b/oox/source/crypto/DocumentEncryption.cxx new file mode 100644 index 000000000000..e0941cfa5d95 --- /dev/null +++ b/oox/source/crypto/DocumentEncryption.cxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include "oox/crypto/DocumentEncryption.hxx" + +#include <com/sun/star/io/XSeekable.hpp> + +#include "oox/helper/binaryinputstream.hxx" +#include "oox/helper/binaryoutputstream.hxx" + +namespace oox { +namespace core { + +using namespace css::beans; +using namespace css::io; +using namespace css::lang; +using namespace css::uno; + +using namespace std; + +DocumentEncryption::DocumentEncryption(Reference< XStream > xDocumentStream, oox::ole::OleStorage& rOleStorage, OUString aPassword) : + mxDocumentStream(xDocumentStream), + mrOleStorage(rOleStorage), + maPassword(aPassword) +{} + +bool DocumentEncryption::encrypt() +{ + Reference< XInputStream > xInputStream ( mxDocumentStream->getInputStream(), UNO_SET_THROW ); + Reference< XSeekable > xSeekable( xInputStream, UNO_QUERY ); + + if (!xSeekable.is()) + return false; + + sal_uInt32 aLength = xSeekable->getLength(); + + if (!mrOleStorage.isStorage()) + return false; + + Reference< XOutputStream > xEncryptionInfo( mrOleStorage.openOutputStream( "EncryptionInfo" ), UNO_SET_THROW ); + BinaryXOutputStream aEncryptionInfoBinaryOutputStream( xEncryptionInfo, false ); + + mEngine.writeEncryptionInfo(maPassword, 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.writeValue<sal_uInt32>( aLength ); // size + aEncryptedPackageStream.writeValue<sal_uInt32>( 0 ); // reserved + + mEngine.encrypt(aDocumentInputStream, aEncryptedPackageStream); + + aEncryptedPackageStream.close(); + aDocumentInputStream.close(); + + xEncryptedPackage->flush(); + xEncryptedPackage->closeOutput(); + + return true; +} + +} // namespace core +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/crypto/Standard2007Engine.cxx b/oox/source/crypto/Standard2007Engine.cxx new file mode 100644 index 000000000000..3c17bb60907c --- /dev/null +++ b/oox/source/crypto/Standard2007Engine.cxx @@ -0,0 +1,288 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include "oox/crypto/Standard2007Engine.hxx" + +#include <osl/time.h> +#include <rtl/random.h> + +namespace oox { +namespace core { + +using namespace std; + +/* =========================================================================== */ +/* Kudos to Caolan McNamara who provided the core decryption implementations. */ +/* =========================================================================== */ +namespace +{ + +void lclRandomGenerateValues(sal_uInt8* aArray, sal_uInt32 aSize) +{ + TimeValue aTime; + osl_getSystemTime( &aTime ); + rtlRandomPool aRandomPool = rtl_random_createPool (); + rtl_random_addBytes ( aRandomPool, &aTime, 8 ); + rtl_random_getBytes ( aRandomPool, aArray, aSize ); + rtl_random_destroyPool ( aRandomPool ); +} + +static const OUString lclCspName = "Microsoft Enhanced RSA and AES Cryptographic Provider"; + +} // namespace + +EncryptionStandardHeader::EncryptionStandardHeader() +{ + flags = 0; + sizeExtra = 0; + algId = 0; + algIdHash = 0; + keyBits = 0; + providedType = 0; + reserved1 = 0; + reserved2 = 0; +} + +EncryptionVerifierAES::EncryptionVerifierAES() +{ + saltSize = SALT_LENGTH; + memset(salt, 0, sizeof(salt)); + memset(encryptedVerifier, 0, sizeof(encryptedVerifier)); + memset(encryptedVerifierHash, 0, sizeof(encryptedVerifierHash)); +} + +Standard2007Engine::Standard2007Engine() : + CryptoEngine() +{} + +Standard2007Engine::~Standard2007Engine() +{} + +StandardEncryptionInfo& Standard2007Engine::getInfo() +{ + return mInfo; +} + +bool Standard2007Engine::generateVerifier() +{ + // only support key of size 128 bit (16 byte) + if (mKey.size() != 16) + return false; + + sal_uInt32 outputLength; + vector<sal_uInt8> verifier(ENCRYPTED_VERIFIER_LENGTH); + vector<sal_uInt8> encryptedVerifier(ENCRYPTED_VERIFIER_LENGTH); + + lclRandomGenerateValues(&verifier[0], verifier.size()); + + vector<sal_uInt8> iv; + Encrypt aEncryptorVerifier(mKey, iv, Crypto::AES_128_ECB); + outputLength = aEncryptorVerifier.update(encryptedVerifier, verifier); + if (outputLength != ENCRYPTED_VERIFIER_LENGTH) + return false; + std::copy(encryptedVerifier.begin(), encryptedVerifier.end(), mInfo.verifier.encryptedVerifier); + + vector<sal_uInt8> hash(RTL_DIGEST_LENGTH_SHA1, 0); + mInfo.verifier.encryptedVerifierHashSize = RTL_DIGEST_LENGTH_SHA1; + sha1(hash, verifier); + hash.resize(ENCRYPTED_VERIFIER_HASH_LENGTH, 0); + + vector<sal_uInt8> encryptedHash(ENCRYPTED_VERIFIER_HASH_LENGTH, 0); + + Encrypt aEncryptorHash(mKey, iv, Crypto::AES_128_ECB); + outputLength = aEncryptorHash.update(encryptedHash, hash, hash.size()); + std::copy(encryptedHash.begin(), encryptedHash.end(), mInfo.verifier.encryptedVerifierHash); + + return true; +} + +bool Standard2007Engine::calculateEncryptionKey(const OUString& rPassword) +{ + sal_uInt32 saltSize = mInfo.verifier.saltSize; + sal_uInt32 passwordByteLength = rPassword.getLength() * 2; + const sal_uInt8* saltArray = mInfo.verifier.salt; + + // Prepare initial data -> salt + password (in 16-bit chars) + vector<sal_uInt8> initialData(saltSize + passwordByteLength); + std::copy(saltArray, saltArray + saltSize, initialData.begin()); + + const sal_uInt8* passwordByteArray = reinterpret_cast<const sal_uInt8*>(rPassword.getStr()); + + std::copy( + passwordByteArray, + passwordByteArray + passwordByteLength, + initialData.begin() + saltSize); + + // use "hash" vector for result of sha1 hashing + vector<sal_uInt8> hash(RTL_DIGEST_LENGTH_SHA1, 0); + + // calculate SHA1 hash of initialData + sha1(hash, initialData); + + // data = iterator (4bytes) + hash + vector<sal_uInt8> data(RTL_DIGEST_LENGTH_SHA1 + 4, 0); + + for (int i = 0; i < 50000; i++) + { + ByteOrderConverter::writeLittleEndian( &data[0], i ); + std::copy(hash.begin(), hash.end(), data.begin() + 4); + sha1(hash, data); + } + std::copy(hash.begin(), hash.end(), data.begin() ); + std::fill(data.begin() + RTL_DIGEST_LENGTH_SHA1, data.end(), 0 ); + + sha1(hash, data); + + // derive key + vector<sal_uInt8> buffer(64, 0x36); + for( sal_uInt32 i = 0; i < hash.size(); ++i ) + buffer[i] ^= hash[i]; + + sha1(hash, buffer); + std::copy(hash.begin(), hash.begin() + mKey.size(), mKey.begin()); + + return true; +} + +bool Standard2007Engine::generateEncryptionKey(const OUString& password) +{ + mKey.clear(); + mKey.resize(mInfo.header.keyBits / 8, 0); + + calculateEncryptionKey(password); + + vector<sal_uInt8> encryptedVerifier(ENCRYPTED_VERIFIER_LENGTH); + std::copy( + mInfo.verifier.encryptedVerifier, + mInfo.verifier.encryptedVerifier + ENCRYPTED_VERIFIER_LENGTH, + encryptedVerifier.begin()); + + vector<sal_uInt8> encryptedVerifierHash(ENCRYPTED_VERIFIER_HASH_LENGTH); + std::copy( + mInfo.verifier.encryptedVerifierHash, + mInfo.verifier.encryptedVerifierHash + ENCRYPTED_VERIFIER_HASH_LENGTH, + encryptedVerifierHash.begin()); + + return checkEncryptionData( + mKey, mKey.size(), + encryptedVerifier, encryptedVerifier.size(), + encryptedVerifierHash, encryptedVerifierHash.size() ); +} + +bool Standard2007Engine::decrypt( + BinaryXInputStream& aInputStream, + BinaryXOutputStream& aOutputStream) +{ + sal_uInt32 totalSize; + aInputStream >> totalSize; // Document unencrypted size - 4 bytes + aInputStream.skip( 4 ); // Reserved 4 Bytes + + vector<sal_uInt8> iv; + Decrypt aDecryptor(mKey, iv, Crypto::AES_128_ECB); + vector<sal_uInt8> inputBuffer (4096); + vector<sal_uInt8> outputBuffer(4096); + sal_uInt32 inputLength; + sal_uInt32 outputLength; + + while( (inputLength = aInputStream.readMemory( &inputBuffer[0], inputBuffer.size() )) > 0 ) + { + outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength); + aOutputStream.writeMemory( &outputBuffer[0], outputLength ); + } + return true; +} + +bool Standard2007Engine::checkEncryptionData( + vector<sal_uInt8> key, sal_uInt32 keySize, + vector<sal_uInt8> encryptedVerifier, sal_uInt32 verifierSize, + vector<sal_uInt8> encryptedHash, sal_uInt32 hashSize ) +{ + // the only currently supported algorithm needs key size 128 + if ( keySize != 16 || verifierSize != 16 ) + return false; + + vector<sal_uInt8> verifier(verifierSize, 0); + Decrypt::aes128ecb(verifier, encryptedVerifier, key); + + vector<sal_uInt8> verifierHash(hashSize, 0); + Decrypt::aes128ecb(verifierHash, encryptedHash, key); + + vector<sal_uInt8> hash(RTL_DIGEST_LENGTH_SHA1, 0); + sha1(hash, verifier); + + return std::equal( hash.begin(), hash.end(), verifierHash.begin() ); +} + +bool Standard2007Engine::writeEncryptionInfo(const OUString& password, BinaryXOutputStream& rStream) +{ + mInfo.header.flags = ENCRYPTINFO_AES | ENCRYPTINFO_CRYPTOAPI; + mInfo.header.algId = ENCRYPT_ALGO_AES128; + mInfo.header.algIdHash = ENCRYPT_HASH_SHA1; + mInfo.header.keyBits = ENCRYPT_KEY_SIZE_AES_128; + mInfo.header.providedType = ENCRYPT_PROVIDER_TYPE_AES; + + lclRandomGenerateValues(mInfo.verifier.salt, mInfo.verifier.saltSize); + const sal_Int32 keyLength = mInfo.header.keyBits / 8; + + mKey.clear(); + mKey.resize(keyLength, 0); + + if (!calculateEncryptionKey(password)) + return false; + + if (!generateVerifier()) + return false; + + rStream.writeValue(VERSION_INFO_2007_FORMAT); + + sal_uInt32 cspNameSize = (lclCspName.getLength() * 2) + 2; + + sal_uInt32 encryptionHeaderSize = static_cast<sal_uInt32>(sizeof(EncryptionStandardHeader)); + + rStream << mInfo.header.flags; + sal_uInt32 headerSize = encryptionHeaderSize + cspNameSize; + rStream << headerSize; + + rStream.writeMemory(&mInfo.header, encryptionHeaderSize); + rStream.writeUnicodeArray(lclCspName); + rStream.writeValue<sal_uInt16>(0); + + sal_uInt32 encryptionVerifierSize = static_cast<sal_uInt32>(sizeof(EncryptionVerifierAES)); + rStream.writeMemory(&mInfo.verifier, encryptionVerifierSize); + + return true; +} + +bool Standard2007Engine::encrypt( + BinaryXInputStream& aInputStream, + BinaryXOutputStream& aOutputStream) +{ + vector<sal_uInt8> inputBuffer(1024); + vector<sal_uInt8> outputBuffer(1024); + + sal_uInt32 inputLength; + sal_uInt32 outputLength; + + vector<sal_uInt8> iv; + Encrypt aEncryptor(mKey, iv, Crypto::AES_128_ECB); + + while( (inputLength = aInputStream.readMemory( &inputBuffer[0], inputBuffer.size() )) > 0 ) + { + inputLength = inputLength % 16 == 0 ? inputLength : ((inputLength / 16) * 16) + 16; + outputLength = aEncryptor.update(outputBuffer, inputBuffer, inputLength); + aOutputStream.writeMemory( &outputBuffer[0], outputLength ); + } + return true; +} + +} // namespace core +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |