diff --git a/include/xlnt/cell/cell.hpp b/include/xlnt/cell/cell.hpp
index 89a1c0ed..bd555336 100644
--- a/include/xlnt/cell/cell.hpp
+++ b/include/xlnt/cell/cell.hpp
@@ -183,9 +183,20 @@ public:
std::string hyperlink() const;
///
- /// Adds a hyperlink to this cell pointing to the URI of the given value.
+ /// Adds a hyperlink to this cell pointing to the URL of the given value.
///
- void hyperlink(const std::string &value);
+ void hyperlink(const std::string &url);
+
+ ///
+ /// Adds a hyperlink to this cell pointing to the URI of the given value and sets
+ /// the text value of the cell to the given parameter.
+ ///
+ void hyperlink(const std::string &url, const std::string &display);
+
+ ///
+ /// Adds an internal hyperlink to this cell pointing to the given cell.
+ ///
+ void hyperlink(xlnt::cell target);
///
/// Returns true if this cell has a hyperlink set.
diff --git a/include/xlnt/workbook/workbook.hpp b/include/xlnt/workbook/workbook.hpp
index d7b8f29d..d4f00c06 100644
--- a/include/xlnt/workbook/workbook.hpp
+++ b/include/xlnt/workbook/workbook.hpp
@@ -450,20 +450,20 @@ public:
/// Serializes the workbook into an XLSX file encrypted with the given password
/// and loads the bytes into a file named filename.
///
- void save(const std::string &filename, const std::string &password);
+ void save(const std::string &filename, const std::string &password) const;
#ifdef _MSC_VER
///
/// Serializes the workbook into an XLSX file and saves the data into a file
/// named filename.
///
- void save(const std::wstring &filename);
+ void save(const std::wstring &filename) const;
///
/// Serializes the workbook into an XLSX file encrypted with the given password
/// and loads the bytes into a file named filename.
///
- void save(const std::wstring &filename, const std::string &password);
+ void save(const std::wstring &filename, const std::string &password) const;
#endif
///
@@ -476,7 +476,7 @@ public:
/// Serializes the workbook into an XLSX file encrypted with the given password
/// and loads the bytes into a file named filename.
///
- void save(const xlnt::path &filename, const std::string &password);
+ void save(const xlnt::path &filename, const std::string &password) const;
///
/// Serializes the workbook into an XLSX file and saves the data into stream.
@@ -487,7 +487,7 @@ public:
/// Serializes the workbook into an XLSX file encrypted with the given password
/// and loads the bytes into the given stream.
///
- void save(std::ostream &stream, const std::string &password);
+ void save(std::ostream &stream, const std::string &password) const;
///
/// Interprets byte vector data as an XLSX file and sets the content of this
diff --git a/include/xlnt/worksheet/worksheet.hpp b/include/xlnt/worksheet/worksheet.hpp
index b731301c..7a231da9 100644
--- a/include/xlnt/worksheet/worksheet.hpp
+++ b/include/xlnt/worksheet/worksheet.hpp
@@ -742,6 +742,11 @@ private:
///
void register_comments_in_manifest();
+ ///
+ /// Add the calcChain part to the workbook if it doesn't already exist.
+ ///
+ void register_calc_chain_in_manifest();
+
///
/// Removes calcChain part from manifest if no formulae remain in workbook.
///
diff --git a/source/cell/cell.cpp b/source/cell/cell.cpp
index a53a9fb4..0dd78fd0 100644
--- a/source/cell/cell.cpp
+++ b/source/cell/cell.cpp
@@ -446,18 +446,24 @@ void cell::hyperlink(const std::string &hyperlink)
}
d_->hyperlink_ = hyperlink;
+}
- if (data_type() == type::null)
- {
- value(hyperlink);
- }
+void cell::hyperlink(const std::string &url, const std::string &display)
+{
+ hyperlink(url);
+ value(display);
+}
+
+void cell::hyperlink(xlnt::cell /*target*/)
+{
+ //todo: implement
}
void cell::formula(const std::string &formula)
{
if (formula.empty())
{
- throw invalid_parameter();
+ return clear_formula();
}
if (formula[0] == '=')
@@ -468,6 +474,8 @@ void cell::formula(const std::string &formula)
{
d_->formula_ = formula;
}
+
+ worksheet().register_calc_chain_in_manifest();
}
bool cell::has_formula() const
diff --git a/source/cell/tests/test_cell.hpp b/source/cell/tests/test_cell.hpp
index 0e4a6102..81140c42 100644
--- a/source/cell/tests/test_cell.hpp
+++ b/source/cell/tests/test_cell.hpp
@@ -140,7 +140,7 @@ public:
auto cell = ws.cell(xlnt::cell_reference(1, 1));
TS_ASSERT(!cell.has_formula());
- TS_ASSERT_THROWS(cell.formula(""), xlnt::invalid_parameter);
+ TS_ASSERT_THROWS_NOTHING(cell.formula(""));
TS_ASSERT(!cell.has_formula());
cell.formula("=42");
TS_ASSERT(cell.has_formula());
diff --git a/source/detail/workbook_impl.hpp b/source/detail/workbook_impl.hpp
index 8c37fec8..68d06a33 100644
--- a/source/detail/workbook_impl.hpp
+++ b/source/detail/workbook_impl.hpp
@@ -95,9 +95,9 @@ struct workbook_impl
optional theme_;
std::unordered_map> images_;
- std::unordered_map> core_properties_;
- std::unordered_map> extended_properties_;
- std::unordered_map custom_properties_;
+ std::vector> core_properties_;
+ std::vector> extended_properties_;
+ std::vector> custom_properties_;
std::unordered_map sheet_title_rel_id_map_;
diff --git a/source/detail/xlsx_crypto.cpp b/source/detail/xlsx_crypto.cpp
index 8282b2d5..790b5d26 100644
--- a/source/detail/xlsx_crypto.cpp
+++ b/source/detail/xlsx_crypto.cpp
@@ -21,37 +21,20 @@
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
-#include
+#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+namespace {
-namespace xlnt {
-namespace detail {
-
-enum class hash_algorithm
+template
+auto read_int(std::size_t &index, const std::vector &raw_data)
{
- sha1,
- sha256,
- sha384,
- sha512,
- md5,
- md4,
- md2,
- ripemd128,
- ripemd160,
- whirlpool
-};
+ auto result = *reinterpret_cast(&raw_data[index]);
+ index += sizeof(T);
-} // namespace detail
-} // namespace xlnt
+ return result;
+}
+
+} // namespace
namespace xml {
@@ -116,654 +99,571 @@ struct value_traits
namespace xlnt {
namespace detail {
-struct crypto_helper
+std::vector crypto_helper::aes(
+ const std::vector &key,
+ const std::vector &iv,
+ const std::vector &source,
+ cipher_chaining chaining, cipher_direction direction)
{
- static const std::size_t segment_length;
+ std::vector destination(source.size(), 0);
- enum class cipher_algorithm
+ if (direction == cipher_direction::encryption && chaining == cipher_chaining::cbc)
{
- aes,
- rc2,
- rc4,
- des,
- desx,
- triple_des,
- triple_des_112
- };
+ CryptoPP::AES::Encryption aesEncryption(key.data(), key.size());
+ CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv.data());
- enum class cipher_chaining
+ CryptoPP::ArraySource as(
+ source.data(), source.size(), true, new CryptoPP::StreamTransformationFilter(cbcEncryption,
+ new CryptoPP::ArraySink(destination.data(), destination.size()),
+ CryptoPP::BlockPaddingSchemeDef::NO_PADDING));
+ }
+ else if (direction == cipher_direction::decryption && chaining == cipher_chaining::cbc)
{
- ecb, // electronic code book
- cbc, // cipher block chaining
- cfb // cipher feedback chaining
- };
+ CryptoPP::AES::Decryption aesDecryption(key.data(), key.size());
+ CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data());
- enum class cipher_direction
+ CryptoPP::ArraySource as(
+ source.data(), source.size(), true, new CryptoPP::StreamTransformationFilter(cbcDecryption,
+ new CryptoPP::ArraySink(destination.data(), destination.size()),
+ CryptoPP::BlockPaddingSchemeDef::NO_PADDING));
+ }
+ else if (direction == cipher_direction::encryption && chaining == cipher_chaining::ecb)
{
- encryption,
- decryption
- };
+ CryptoPP::AES::Encryption aesEncryption(key.data(), key.size());
+ CryptoPP::ECB_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv.data());
- static std::vector aes(const std::vector &key, const std::vector &iv,
- const std::vector &source, cipher_chaining chaining, cipher_direction direction)
+ CryptoPP::ArraySource as(
+ source.data(), source.size(), true, new CryptoPP::StreamTransformationFilter(cbcEncryption,
+ new CryptoPP::ArraySink(destination.data(), destination.size()),
+ CryptoPP::BlockPaddingSchemeDef::NO_PADDING));
+ }
+ else if (direction == cipher_direction::decryption && chaining == cipher_chaining::ecb)
{
- std::vector destination(source.size(), 0);
+ CryptoPP::AES::Decryption aesDecryption(key.data(), key.size());
+ CryptoPP::ECB_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data());
- if (direction == cipher_direction::encryption && chaining == cipher_chaining::cbc)
- {
- CryptoPP::AES::Encryption aesEncryption(key.data(), key.size());
- CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv.data());
-
- CryptoPP::ArraySource as(
- source.data(), source.size(), true, new CryptoPP::StreamTransformationFilter(cbcEncryption,
- new CryptoPP::ArraySink(destination.data(), destination.size()),
- CryptoPP::BlockPaddingSchemeDef::NO_PADDING));
- }
- else if (direction == cipher_direction::decryption && chaining == cipher_chaining::cbc)
- {
- CryptoPP::AES::Decryption aesDecryption(key.data(), key.size());
- CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data());
-
- CryptoPP::ArraySource as(
- source.data(), source.size(), true, new CryptoPP::StreamTransformationFilter(cbcDecryption,
- new CryptoPP::ArraySink(destination.data(), destination.size()),
- CryptoPP::BlockPaddingSchemeDef::NO_PADDING));
- }
- else if (direction == cipher_direction::encryption && chaining == cipher_chaining::ecb)
- {
- CryptoPP::AES::Encryption aesEncryption(key.data(), key.size());
- CryptoPP::ECB_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv.data());
-
- CryptoPP::ArraySource as(
- source.data(), source.size(), true, new CryptoPP::StreamTransformationFilter(cbcEncryption,
- new CryptoPP::ArraySink(destination.data(), destination.size()),
- CryptoPP::BlockPaddingSchemeDef::NO_PADDING));
- }
- else if (direction == cipher_direction::decryption && chaining == cipher_chaining::ecb)
- {
- CryptoPP::AES::Decryption aesDecryption(key.data(), key.size());
- CryptoPP::ECB_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data());
-
- CryptoPP::ArraySource as(
- source.data(), source.size(), true, new CryptoPP::StreamTransformationFilter(cbcDecryption,
- new CryptoPP::ArraySink(destination.data(), destination.size()),
- CryptoPP::BlockPaddingSchemeDef::NO_PADDING));
- }
-
- return destination;
+ CryptoPP::ArraySource as(
+ source.data(), source.size(), true, new CryptoPP::StreamTransformationFilter(cbcDecryption,
+ new CryptoPP::ArraySink(destination.data(), destination.size()),
+ CryptoPP::BlockPaddingSchemeDef::NO_PADDING));
}
- static std::vector decode_base64(const std::string &encoded)
+ return destination;
+}
+
+std::vector crypto_helper::decode_base64(const std::string &encoded)
+{
+ CryptoPP::Base64Decoder decoder;
+ decoder.Put(reinterpret_cast(encoded.data()), encoded.size());
+ decoder.MessageEnd();
+
+ std::vector decoded(decoder.MaxRetrievable(), 0);
+ decoder.Get(decoded.data(), decoded.size());
+
+ return decoded;
+}
+
+std::string crypto_helper::encode_base64(const std::vector &decoded)
+{
+ CryptoPP::Base64Decoder encoder;
+ encoder.Put(reinterpret_cast(decoded.data()), decoded.size());
+ encoder.MessageEnd();
+
+ std::vector encoded(encoder.MaxRetrievable(), 0);
+ encoder.Get(encoded.data(), encoded.size());
+
+ return std::string(encoded.begin(), encoded.end());
+}
+
+std::vector crypto_helper::hash(hash_algorithm algorithm, const std::vector &input)
+{
+ std::vector digest;
+
+ if (algorithm == hash_algorithm::sha512)
{
- CryptoPP::Base64Decoder decoder;
- decoder.Put(reinterpret_cast(encoded.data()), encoded.size());
- decoder.MessageEnd();
-
- std::vector decoded(decoder.MaxRetrievable(), 0);
- decoder.Get(decoded.data(), decoded.size());
-
- return decoded;
+ CryptoPP::SHA512 sha512;
+ digest.resize(CryptoPP::SHA512::DIGESTSIZE, 0);
+ sha512.CalculateDigest(digest.data(), input.data(), input.size());
+ }
+ else if (algorithm == hash_algorithm::sha1)
+ {
+ CryptoPP::SHA1 sha1;
+ digest.resize(CryptoPP::SHA1::DIGESTSIZE, 0);
+ sha1.CalculateDigest(digest.data(), input.data(), input.size());
}
- static std::string encode_base64(const std::vector &decoded)
+ return digest;
+}
+
+std::vector crypto_helper::file(POLE::Storage &storage, const std::string &name)
+{
+ POLE::Stream stream(&storage, name.c_str());
+ if (stream.fail()) return {};
+ std::vector bytes(stream.size(), 0);
+ stream.read(bytes.data(), static_cast(bytes.size()));
+ return bytes;
+}
+
+std::vector crypto_helper::decrypt_xlsx_standard(
+ const std::vector &encryption_info,
+ const std::string &password,
+ const std::vector &encrypted_package)
+{
+ std::size_t offset = 0;
+
+ standard_encryption_info info;
+
+ auto header_length = read_int(offset, encryption_info);
+ auto index_at_start = offset;
+ /*auto skip_flags = */ read_int(offset, encryption_info);
+ /*auto size_extra = */ read_int(offset, encryption_info);
+ auto alg_id = read_int(offset, encryption_info);
+
+ if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610)
{
- CryptoPP::Base64Decoder encoder;
- encoder.Put(reinterpret_cast(decoded.data()), decoded.size());
- encoder.MessageEnd();
-
- std::vector encoded(encoder.MaxRetrievable(), 0);
- encoder.Get(encoded.data(), encoded.size());
-
- return std::string(encoded.begin(), encoded.end());
+ info.cipher = cipher_algorithm::aes;
+ }
+ else
+ {
+ throw xlnt::exception("invalid cipher algorithm");
}
- static std::vector hash(hash_algorithm algorithm, const std::vector &input)
+ auto alg_id_hash = read_int(offset, encryption_info);
+ if (alg_id_hash != 0x00008004 && alg_id_hash == 0)
{
- std::vector digest;
+ throw xlnt::exception("invalid hash algorithm");
+ }
- if (algorithm == hash_algorithm::sha512)
+ info.key_bits = read_int(offset, encryption_info);
+ info.key_bytes = info.key_bits / 8;
+
+ auto provider_type = read_int(offset, encryption_info);
+ if (provider_type != 0 && provider_type != 0x00000018)
+ {
+ throw xlnt::exception("invalid provider type");
+ }
+
+ read_int(offset, encryption_info); // reserved 1
+ if (read_int(offset, encryption_info) != 0) // reserved 2
+ {
+ throw xlnt::exception("invalid header");
+ }
+
+ const auto csp_name_length = header_length - (offset - index_at_start);
+ std::vector csp_name_wide(
+ reinterpret_cast(&*(encryption_info.begin() + static_cast(offset))),
+ reinterpret_cast(
+ &*(encryption_info.begin() + static_cast(offset + csp_name_length))));
+ std::string csp_name(csp_name_wide.begin(), csp_name_wide.end() - 1); // without trailing null
+ if (csp_name != "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
+ && csp_name != "Microsoft Enhanced RSA and AES Cryptographic Provider")
+ {
+ throw xlnt::exception("invalid cryptographic provider");
+ }
+ offset += csp_name_length;
+
+ const auto salt_size = read_int(offset, encryption_info);
+ std::vector salt(encryption_info.begin() + static_cast(offset),
+ encryption_info.begin() + static_cast(offset + salt_size));
+ offset += salt_size;
+
+ static const auto verifier_size = std::size_t(16);
+ std::vector verifier_hash_input(encryption_info.begin() + static_cast(offset),
+ encryption_info.begin() + static_cast(offset + verifier_size));
+ offset += verifier_size;
+
+ const auto verifier_hash_size = read_int(offset, encryption_info);
+ std::vector verifier_hash_value(encryption_info.begin() + static_cast(offset),
+ encryption_info.begin() + static_cast(offset + verifier_hash_size));
+ offset += verifier_hash_size;
+
+ // begin key generation algorithm
+
+ // H_0 = H(salt + password)
+ auto salt_plus_password = salt;
+ std::vector password_wide(password.begin(), password.end());
+ std::for_each(password_wide.begin(), password_wide.end(), [&salt_plus_password](std::uint16_t c) {
+ salt_plus_password.insert(salt_plus_password.end(), reinterpret_cast(&c),
+ reinterpret_cast(&c) + sizeof(std::uint16_t));
+ });
+ std::vector h_0 = hash(info.hash, salt_plus_password);
+
+ // H_n = H(iterator + H_n-1)
+ std::vector iterator_plus_h_n(4, 0);
+ iterator_plus_h_n.insert(iterator_plus_h_n.end(), h_0.begin(), h_0.end());
+ std::uint32_t &iterator = *reinterpret_cast(iterator_plus_h_n.data());
+ std::vector h_n;
+ for (iterator = 0; iterator < info.spin_count; ++iterator)
+ {
+ h_n = hash(info.hash, iterator_plus_h_n);
+ std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4);
+ }
+
+ // H_final = H(H_n + block)
+ auto h_n_plus_block = h_n;
+ const std::uint32_t block_number = 0;
+ h_n_plus_block.insert(h_n_plus_block.end(), reinterpret_cast(&block_number),
+ reinterpret_cast(&block_number) + sizeof(std::uint32_t));
+ auto h_final = hash(info.hash, h_n_plus_block);
+
+ // X1 = H(h_final ^ 0x36)
+ std::vector buffer(64, 0x36);
+ for (std::size_t i = 0; i < h_final.size(); ++i)
+ {
+ buffer[i] = static_cast(0x36 ^ h_final[i]);
+ }
+ auto X1 = hash(info.hash, buffer);
+
+ // X2 = H(h_final ^ 0x5C)
+ buffer.assign(64, 0x5c);
+ for (std::size_t i = 0; i < h_final.size(); ++i)
+ {
+ buffer[i] = static_cast(0x5c ^ h_final[i]);
+ }
+ auto X2 = hash(info.hash, buffer);
+
+ auto X3 = X1;
+ X3.insert(X3.end(), X2.begin(), X2.end());
+
+ auto key_derived =
+ std::vector(X3.begin(), X3.begin() + static_cast(info.key_bytes));
+
+ // todo: verify here
+
+ std::size_t package_offset = 0;
+ auto decrypted_size = static_cast(read_int(package_offset, encrypted_package));
+ auto decrypted = aes(key_derived, {},
+ std::vector(encrypted_package.begin() + 8, encrypted_package.end()),
+ cipher_chaining::ecb, cipher_direction::decryption);
+ decrypted.resize(decrypted_size);
+
+ return decrypted;
+}
+
+crypto_helper::agile_encryption_info crypto_helper::generate_agile_encryption_info(const std::string &password)
+{
+ agile_encryption_info result;
+ result.key_data.salt_value.assign(password.begin(), password.end());
+ return result;
+}
+
+std::vector crypto_helper::write_agile_encryption_info(const std::string &password)
+{
+ static const auto &xmlns = xlnt::constants::ns("encryption");
+ static const auto &xmlns_p = xlnt::constants::ns("encryption-password");
+
+ std::vector encryption_info;
+ xlnt::detail::vector_ostreambuf encryption_info_buffer(encryption_info);
+ std::ostream encryption_info_stream(&encryption_info_buffer);
+ xml::serializer serializer(encryption_info_stream, "EncryptionInfo");
+
+ agile_encryption_info result = generate_agile_encryption_info(password);
+
+ serializer.start_element(xmlns, "encryption");
+
+ serializer.start_element(xmlns, "keyData");
+ serializer.attribute("saltSize", result.key_data.salt_size);
+ serializer.attribute("blockSize", result.key_data.block_size);
+ serializer.attribute("keyBits", result.key_data.key_bits);
+ serializer.attribute("hashSize", result.key_data.hash_size);
+ serializer.attribute("cipherAlgorithm", result.key_data.cipher_algorithm);
+ serializer.attribute("cipherChaining", result.key_data.cipher_chaining);
+ serializer.attribute("hashAlgorithm", result.key_data.hash_algorithm);
+ serializer.attribute("saltValue", encode_base64(result.key_data.salt_value));
+ serializer.end_element(xmlns, "keyData");
+
+ serializer.start_element(xmlns, "dataIntegrity");
+ serializer.attribute("encryptedHmacKey", encode_base64(result.data_integrity.hmac_key));
+ serializer.attribute("encryptedHmacValue", encode_base64(result.data_integrity.hmac_value));
+ serializer.end_element(xmlns, "dataIntegrity");
+
+ serializer.start_element(xmlns, "keyEncryptors");
+ serializer.start_element(xmlns, "keyEncryptor");
+ serializer.attribute("uri", "");
+ serializer.start_element(xmlns_p, "encryptedKey");
+ serializer.attribute("spinCount", result.key_encryptor.spin_count);
+ serializer.attribute("saltSize", result.key_encryptor.salt_size);
+ serializer.attribute("blockSize", result.key_encryptor.block_size);
+ serializer.attribute("keyBits", result.key_encryptor.key_bits);
+ serializer.attribute("hashSize", result.key_encryptor.hash_size);
+ serializer.attribute("cipherAlgorithm", result.key_encryptor.cipher_algorithm);
+ serializer.attribute("cipherChaining", result.key_encryptor.cipher_chaining);
+ serializer.attribute("hashAlgorithm", result.key_encryptor.hash);
+ serializer.attribute("saltValue", encode_base64(result.key_encryptor.salt_value));
+ serializer.attribute("encryptedVerifierHashInput", encode_base64(result.key_encryptor.verifier_hash_input));
+ serializer.attribute("encryptedVerifierHashValue", encode_base64(result.key_encryptor.verifier_hash_value));
+ serializer.attribute("encryptedKeyValue", encode_base64(result.key_encryptor.encrypted_key_value));
+ serializer.end_element(xmlns_p, "encryptedKey");
+ serializer.end_element(xmlns, "keyEncryptor");
+ serializer.end_element(xmlns, "keyEncryptors");
+
+ serializer.end_element(xmlns, "encryption");
+
+ return encryption_info;
+}
+
+std::vector crypto_helper::decrypt_xlsx_agile(
+ const std::vector &encryption_info,
+ const std::string &password,
+ const std::vector &encrypted_package)
+{
+ static const auto &xmlns = xlnt::constants::ns("encryption");
+ static const auto &xmlns_p = xlnt::constants::ns("encryption-password");
+ // static const auto &xmlns_c = xlnt::constants::namespace_("encryption-certificate");
+
+ agile_encryption_info result;
+
+ xml::parser parser(encryption_info.data(), encryption_info.size(), "EncryptionInfo");
+
+ parser.next_expect(xml::parser::event_type::start_element, xmlns, "encryption");
+
+ parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyData");
+ result.key_data.salt_size = parser.attribute("saltSize");
+ result.key_data.block_size = parser.attribute("blockSize");
+ result.key_data.key_bits = parser.attribute("keyBits");
+ result.key_data.hash_size = parser.attribute("hashSize");
+ result.key_data.cipher_algorithm = parser.attribute("cipherAlgorithm");
+ result.key_data.cipher_chaining = parser.attribute("cipherChaining");
+ result.key_data.hash_algorithm = parser.attribute("hashAlgorithm");
+ result.key_data.salt_value = decode_base64(parser.attribute("saltValue"));
+ parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyData");
+
+ parser.next_expect(xml::parser::event_type::start_element, xmlns, "dataIntegrity");
+ result.data_integrity.hmac_key = decode_base64(parser.attribute("encryptedHmacKey"));
+ result.data_integrity.hmac_value = decode_base64(parser.attribute("encryptedHmacValue"));
+ parser.next_expect(xml::parser::event_type::end_element, xmlns, "dataIntegrity");
+
+ parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptors");
+ parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptor");
+ parser.attribute("uri");
+ bool any_password_key = false;
+
+ while (parser.peek() != xml::parser::event_type::end_element)
+ {
+ parser.next_expect(xml::parser::event_type::start_element);
+
+ if (parser.namespace_() == xmlns_p && parser.name() == "encryptedKey")
{
- CryptoPP::SHA512 sha512;
- digest.resize(CryptoPP::SHA512::DIGESTSIZE, 0);
- sha512.CalculateDigest(digest.data(), input.data(), input.size());
- }
- else if (algorithm == hash_algorithm::sha1)
- {
- CryptoPP::SHA1 sha1;
- digest.resize(CryptoPP::SHA1::DIGESTSIZE, 0);
- sha1.CalculateDigest(digest.data(), input.data(), input.size());
- }
+ any_password_key = true;
+ result.key_encryptor.spin_count = parser.attribute("spinCount");
+ result.key_encryptor.salt_size = parser.attribute("saltSize");
+ result.key_encryptor.block_size = parser.attribute("blockSize");
+ result.key_encryptor.key_bits = parser.attribute("keyBits");
+ result.key_encryptor.hash_size = parser.attribute("hashSize");
+ result.key_encryptor.cipher_algorithm = parser.attribute("cipherAlgorithm");
+ result.key_encryptor.cipher_chaining = parser.attribute("cipherChaining");
- return digest;
- }
+ auto hash_algorithm_string = parser.attribute("hashAlgorithm");
+ if (hash_algorithm_string == "SHA512")
+ {
+ result.key_encryptor.hash = hash_algorithm::sha512;
+ }
+ else if (hash_algorithm_string == "SHA1")
+ {
+ result.key_encryptor.hash = hash_algorithm::sha1;
+ }
+ else if (hash_algorithm_string == "SHA256")
+ {
+ result.key_encryptor.hash = hash_algorithm::sha256;
+ }
+ else if (hash_algorithm_string == "SHA384")
+ {
+ result.key_encryptor.hash = hash_algorithm::sha384;
+ }
- static std::vector file(POLE::Storage &storage, const std::string &name)
- {
- POLE::Stream stream(&storage, name.c_str());
- if (stream.fail()) return {};
- std::vector bytes(stream.size(), 0);
- stream.read(bytes.data(), static_cast(bytes.size()));
- return bytes;
- }
-
- template
- static auto read_int(std::size_t &index, const std::vector &raw_data)
- {
- auto result = *reinterpret_cast(&raw_data[index]);
- index += sizeof(T);
-
- return result;
- }
-
- struct standard_encryption_info
- {
- const std::size_t spin_count = 50000;
- std::size_t block_size;
- std::size_t key_bits;
- std::size_t key_bytes;
- std::size_t hash_size;
- cipher_algorithm cipher;
- cipher_chaining chaining;
- const hash_algorithm hash = hash_algorithm::sha1;
- std::vector salt_value;
- std::vector verifier_hash_input;
- std::vector verifier_hash_value;
- std::vector encrypted_key_value;
- };
-
- static std::vector decrypt_xlsx_standard(const std::vector &encryption_info,
- const std::string &password, const std::vector &encrypted_package)
- {
- std::size_t offset = 0;
-
- standard_encryption_info info;
-
- auto header_length = read_int(offset, encryption_info);
- auto index_at_start = offset;
- /*auto skip_flags = */ read_int(offset, encryption_info);
- /*auto size_extra = */ read_int(offset, encryption_info);
- auto alg_id = read_int(offset, encryption_info);
-
- if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610)
- {
- info.cipher = cipher_algorithm::aes;
+ result.key_encryptor.salt_value = decode_base64(parser.attribute("saltValue"));
+ result.key_encryptor.verifier_hash_input =
+ decode_base64(parser.attribute("encryptedVerifierHashInput"));
+ result.key_encryptor.verifier_hash_value =
+ decode_base64(parser.attribute("encryptedVerifierHashValue"));
+ result.key_encryptor.encrypted_key_value = decode_base64(parser.attribute("encryptedKeyValue"));
}
else
{
- throw xlnt::exception("invalid cipher algorithm");
+ throw xlnt::unsupported("other encryption key types not supported");
}
- auto alg_id_hash = read_int(offset, encryption_info);
- if (alg_id_hash != 0x00008004 && alg_id_hash == 0)
- {
- throw xlnt::exception("invalid hash algorithm");
- }
-
- info.key_bits = read_int(offset, encryption_info);
- info.key_bytes = info.key_bits / 8;
-
- auto provider_type = read_int(offset, encryption_info);
- if (provider_type != 0 && provider_type != 0x00000018)
- {
- throw xlnt::exception("invalid provider type");
- }
-
- read_int(offset, encryption_info); // reserved 1
- if (read_int(offset, encryption_info) != 0) // reserved 2
- {
- throw xlnt::exception("invalid header");
- }
-
- const auto csp_name_length = header_length - (offset - index_at_start);
- std::vector csp_name_wide(
- reinterpret_cast(&*(encryption_info.begin() + static_cast(offset))),
- reinterpret_cast(
- &*(encryption_info.begin() + static_cast(offset + csp_name_length))));
- std::string csp_name(csp_name_wide.begin(), csp_name_wide.end() - 1); // without trailing null
- if (csp_name != "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
- && csp_name != "Microsoft Enhanced RSA and AES Cryptographic Provider")
- {
- throw xlnt::exception("invalid cryptographic provider");
- }
- offset += csp_name_length;
-
- const auto salt_size = read_int(offset, encryption_info);
- std::vector salt(encryption_info.begin() + static_cast(offset),
- encryption_info.begin() + static_cast(offset + salt_size));
- offset += salt_size;
-
- static const auto verifier_size = std::size_t(16);
- std::vector verifier_hash_input(encryption_info.begin() + static_cast(offset),
- encryption_info.begin() + static_cast(offset + verifier_size));
- offset += verifier_size;
-
- const auto verifier_hash_size = read_int(offset, encryption_info);
- std::vector verifier_hash_value(encryption_info.begin() + static_cast(offset),
- encryption_info.begin() + static_cast(offset + verifier_hash_size));
- offset += verifier_hash_size;
-
- // begin key generation algorithm
-
- // H_0 = H(salt + password)
- auto salt_plus_password = salt;
- std::vector password_wide(password.begin(), password.end());
- std::for_each(password_wide.begin(), password_wide.end(), [&salt_plus_password](std::uint16_t c) {
- salt_plus_password.insert(salt_plus_password.end(), reinterpret_cast(&c),
- reinterpret_cast(&c) + sizeof(std::uint16_t));
- });
- std::vector h_0 = hash(info.hash, salt_plus_password);
-
- // H_n = H(iterator + H_n-1)
- std::vector iterator_plus_h_n(4, 0);
- iterator_plus_h_n.insert(iterator_plus_h_n.end(), h_0.begin(), h_0.end());
- std::uint32_t &iterator = *reinterpret_cast(iterator_plus_h_n.data());
- std::vector h_n;
- for (iterator = 0; iterator < info.spin_count; ++iterator)
- {
- h_n = hash(info.hash, iterator_plus_h_n);
- std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4);
- }
-
- // H_final = H(H_n + block)
- auto h_n_plus_block = h_n;
- const std::uint32_t block_number = 0;
- h_n_plus_block.insert(h_n_plus_block.end(), reinterpret_cast(&block_number),
- reinterpret_cast(&block_number) + sizeof(std::uint32_t));
- auto h_final = hash(info.hash, h_n_plus_block);
-
- // X1 = H(h_final ^ 0x36)
- std::vector buffer(64, 0x36);
- for (std::size_t i = 0; i < h_final.size(); ++i)
- {
- buffer[i] = static_cast(0x36 ^ h_final[i]);
- }
- auto X1 = hash(info.hash, buffer);
-
- // X2 = H(h_final ^ 0x5C)
- buffer.assign(64, 0x5c);
- for (std::size_t i = 0; i < h_final.size(); ++i)
- {
- buffer[i] = static_cast(0x5c ^ h_final[i]);
- }
- auto X2 = hash(info.hash, buffer);
-
- auto X3 = X1;
- X3.insert(X3.end(), X2.begin(), X2.end());
-
- auto key_derived =
- std::vector(X3.begin(), X3.begin() + static_cast(info.key_bytes));
-
- // todo: verify here
-
- std::size_t package_offset = 0;
- auto decrypted_size = static_cast(read_int(package_offset, encrypted_package));
- auto decrypted =
- aes(key_derived, {}, std::vector(encrypted_package.begin() + 8, encrypted_package.end()),
- cipher_chaining::ecb, cipher_direction::decryption);
- decrypted.resize(decrypted_size);
-
- return decrypted;
+ parser.next_expect(xml::parser::event_type::end_element);
}
- struct agile_encryption_info
+ if (!any_password_key)
{
- // key data
- struct
- {
- std::size_t salt_size;
- std::size_t block_size;
- std::size_t key_bits;
- std::size_t hash_size;
- std::string cipher_algorithm;
- std::string cipher_chaining;
- std::string hash_algorithm;
- std::vector salt_value;
- } key_data;
+ throw "no password key in keyEncryptors";
+ }
- struct
- {
- std::vector hmac_key;
- std::vector hmac_value;
- } data_integrity;
+ parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyEncryptor");
+ parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyEncryptors");
- struct
- {
- std::size_t spin_count;
- std::size_t salt_size;
- std::size_t block_size;
- std::size_t key_bits;
- std::size_t hash_size;
- std::string cipher_algorithm;
- std::string cipher_chaining;
- hash_algorithm hash;
- std::vector salt_value;
- std::vector verifier_hash_input;
- std::vector verifier_hash_value;
- std::vector encrypted_key_value;
- } key_encryptor;
+ parser.next_expect(xml::parser::event_type::end_element, xmlns, "encryption");
+
+ // begin key generation algorithm
+
+ // H_0 = H(salt + password)
+ auto salt_plus_password = result.key_encryptor.salt_value;
+ std::vector password_wide(password.begin(), password.end());
+
+ std::for_each(password_wide.begin(), password_wide.end(), [&salt_plus_password](std::uint16_t c) {
+ salt_plus_password.insert(salt_plus_password.end(), reinterpret_cast(&c),
+ reinterpret_cast(&c) + sizeof(std::uint16_t));
+ });
+
+ auto h_0 = hash(result.key_encryptor.hash, salt_plus_password);
+
+ // H_n = H(iterator + H_n-1)
+ std::vector iterator_plus_h_n(4, 0);
+ iterator_plus_h_n.insert(iterator_plus_h_n.end(), h_0.begin(), h_0.end());
+ std::uint32_t &iterator = *reinterpret_cast(iterator_plus_h_n.data());
+ std::vector h_n;
+
+ for (iterator = 0; iterator < result.key_encryptor.spin_count; ++iterator)
+ {
+ h_n = hash(result.key_encryptor.hash, iterator_plus_h_n);
+ std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4);
+ }
+
+ static const std::size_t block_size = 8;
+
+ auto calculate_block = [&result](const std::vector &raw_key,
+ const std::array &block, const std::vector &encrypted) {
+ auto combined = raw_key;
+ combined.insert(combined.end(), block.begin(), block.end());
+ auto key = hash(result.key_encryptor.hash, combined);
+ key.resize(result.key_encryptor.key_bits / 8);
+ return aes(key, result.key_encryptor.salt_value, encrypted,
+ cipher_chaining::cbc, cipher_direction::decryption);
};
- static agile_encryption_info generate_agile_encryption_info(const std::string &password)
+ const std::array input_block_key = {{0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79}};
+ auto hash_input = calculate_block(h_n, input_block_key, result.key_encryptor.verifier_hash_input);
+ auto calculated_verifier = hash(result.key_encryptor.hash, hash_input);
+
+ const std::array verifier_block_key = {
+ {0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e}};
+ auto expected_verifier = calculate_block(h_n, verifier_block_key, result.key_encryptor.verifier_hash_value);
+ expected_verifier.resize(calculated_verifier.size());
+
+ if (calculated_verifier.size() != expected_verifier.size()
+ || std::mismatch(calculated_verifier.begin(), calculated_verifier.end(), expected_verifier.begin(),
+ expected_verifier.end())
+ != std::make_pair(calculated_verifier.end(), expected_verifier.end()))
{
- agile_encryption_info result;
- result.key_data.salt_value.assign(password.begin(), password.end());
- return result;
+ throw xlnt::exception("bad password");
}
- static std::vector write_agile_encryption_info(const std::string &password)
+ const std::array key_value_block_key = {
+ {0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6}};
+ auto key = calculate_block(h_n, key_value_block_key, result.key_encryptor.encrypted_key_value);
+
+ auto salt_size = result.key_data.salt_size;
+ auto salt_with_block_key = result.key_data.salt_value;
+ salt_with_block_key.resize(salt_size + sizeof(std::uint32_t), 0);
+
+ auto &segment = *reinterpret_cast(salt_with_block_key.data() + salt_size);
+ auto total_size = static_cast(*reinterpret_cast(encrypted_package.data()));
+
+ std::vector encrypted_segment(segment_length, 0);
+ std::vector decrypted_package;
+ decrypted_package.reserve(encrypted_package.size() - 8);
+
+ for (std::size_t i = 8; i < encrypted_package.size(); i += segment_length)
{
- static const auto &xmlns = xlnt::constants::ns("encryption");
- static const auto &xmlns_p = xlnt::constants::ns("encryption-password");
+ auto iv = hash(result.key_encryptor.hash, salt_with_block_key);
+ iv.resize(16);
- std::vector encryption_info;
- xlnt::detail::vector_ostreambuf encryption_info_buffer(encryption_info);
- std::ostream encryption_info_stream(&encryption_info_buffer);
- xml::serializer serializer(encryption_info_stream, "EncryptionInfo");
+ auto segment_begin = encrypted_package.begin() + static_cast(i);
+ auto current_segment_length = std::min(segment_length, encrypted_package.size() - i);
+ auto segment_end = encrypted_package.begin() + static_cast(i + current_segment_length);
+ encrypted_segment.assign(segment_begin, segment_end);
+ auto decrypted_segment =
+ aes(key, iv, encrypted_segment, cipher_chaining::cbc, cipher_direction::decryption);
+ decrypted_segment.resize(current_segment_length);
- agile_encryption_info result = generate_agile_encryption_info(password);
+ decrypted_package.insert(decrypted_package.end(), decrypted_segment.begin(), decrypted_segment.end());
- serializer.start_element(xmlns, "encryption");
-
- serializer.start_element(xmlns, "keyData");
- serializer.attribute("saltSize", result.key_data.salt_size);
- serializer.attribute("blockSize", result.key_data.block_size);
- serializer.attribute("keyBits", result.key_data.key_bits);
- serializer.attribute("hashSize", result.key_data.hash_size);
- serializer.attribute("cipherAlgorithm", result.key_data.cipher_algorithm);
- serializer.attribute("cipherChaining", result.key_data.cipher_chaining);
- serializer.attribute("hashAlgorithm", result.key_data.hash_algorithm);
- serializer.attribute("saltValue", encode_base64(result.key_data.salt_value));
- serializer.end_element(xmlns, "keyData");
-
- serializer.start_element(xmlns, "dataIntegrity");
- serializer.attribute("encryptedHmacKey", encode_base64(result.data_integrity.hmac_key));
- serializer.attribute("encryptedHmacValue", encode_base64(result.data_integrity.hmac_value));
- serializer.end_element(xmlns, "dataIntegrity");
-
- serializer.start_element(xmlns, "keyEncryptors");
- serializer.start_element(xmlns, "keyEncryptor");
- serializer.attribute("uri", "");
- serializer.start_element(xmlns_p, "encryptedKey");
- serializer.attribute("spinCount", result.key_encryptor.spin_count);
- serializer.attribute("saltSize", result.key_encryptor.salt_size);
- serializer.attribute("blockSize", result.key_encryptor.block_size);
- serializer.attribute("keyBits", result.key_encryptor.key_bits);
- serializer.attribute("hashSize", result.key_encryptor.hash_size);
- serializer.attribute("cipherAlgorithm", result.key_encryptor.cipher_algorithm);
- serializer.attribute("cipherChaining", result.key_encryptor.cipher_chaining);
- serializer.attribute("hashAlgorithm", result.key_encryptor.hash);
- serializer.attribute("saltValue", encode_base64(result.key_encryptor.salt_value));
- serializer.attribute("encryptedVerifierHashInput", encode_base64(result.key_encryptor.verifier_hash_input));
- serializer.attribute("encryptedVerifierHashValue", encode_base64(result.key_encryptor.verifier_hash_value));
- serializer.attribute("encryptedKeyValue", encode_base64(result.key_encryptor.encrypted_key_value));
- serializer.end_element(xmlns_p, "encryptedKey");
- serializer.end_element(xmlns, "keyEncryptor");
- serializer.end_element(xmlns, "keyEncryptors");
-
- serializer.end_element(xmlns, "encryption");
-
- return encryption_info;
+ ++segment;
}
- static std::vector decrypt_xlsx_agile(const std::vector &encryption_info,
- const std::string &password, const std::vector &encrypted_package)
+ decrypted_package.resize(total_size);
+
+ return decrypted_package;
+}
+
+std::vector crypto_helper::decrypt_xlsx(
+ const std::vector &bytes, const std::string &password)
+{
+ if (bytes.empty())
{
- static const auto &xmlns = xlnt::constants::ns("encryption");
- static const auto &xmlns_p = xlnt::constants::ns("encryption-password");
- // static const auto &xmlns_c = xlnt::constants::namespace_("encryption-certificate");
-
- agile_encryption_info result;
-
- xml::parser parser(encryption_info.data(), encryption_info.size(), "EncryptionInfo");
-
- parser.next_expect(xml::parser::event_type::start_element, xmlns, "encryption");
-
- parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyData");
- result.key_data.salt_size = parser.attribute("saltSize");
- result.key_data.block_size = parser.attribute("blockSize");
- result.key_data.key_bits = parser.attribute("keyBits");
- result.key_data.hash_size = parser.attribute("hashSize");
- result.key_data.cipher_algorithm = parser.attribute("cipherAlgorithm");
- result.key_data.cipher_chaining = parser.attribute("cipherChaining");
- result.key_data.hash_algorithm = parser.attribute("hashAlgorithm");
- result.key_data.salt_value = decode_base64(parser.attribute("saltValue"));
- parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyData");
-
- parser.next_expect(xml::parser::event_type::start_element, xmlns, "dataIntegrity");
- result.data_integrity.hmac_key = decode_base64(parser.attribute("encryptedHmacKey"));
- result.data_integrity.hmac_value = decode_base64(parser.attribute("encryptedHmacValue"));
- parser.next_expect(xml::parser::event_type::end_element, xmlns, "dataIntegrity");
-
- parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptors");
- parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptor");
- parser.attribute("uri");
- bool any_password_key = false;
-
- while (parser.peek() != xml::parser::event_type::end_element)
- {
- parser.next_expect(xml::parser::event_type::start_element);
-
- if (parser.namespace_() == xmlns_p && parser.name() == "encryptedKey")
- {
- any_password_key = true;
- result.key_encryptor.spin_count = parser.attribute("spinCount");
- result.key_encryptor.salt_size = parser.attribute("saltSize");
- result.key_encryptor.block_size = parser.attribute("blockSize");
- result.key_encryptor.key_bits = parser.attribute("keyBits");
- result.key_encryptor.hash_size = parser.attribute("hashSize");
- result.key_encryptor.cipher_algorithm = parser.attribute("cipherAlgorithm");
- result.key_encryptor.cipher_chaining = parser.attribute("cipherChaining");
-
- auto hash_algorithm_string = parser.attribute("hashAlgorithm");
- if (hash_algorithm_string == "SHA512")
- {
- result.key_encryptor.hash = hash_algorithm::sha512;
- }
- else if (hash_algorithm_string == "SHA1")
- {
- result.key_encryptor.hash = hash_algorithm::sha1;
- }
- else if (hash_algorithm_string == "SHA256")
- {
- result.key_encryptor.hash = hash_algorithm::sha256;
- }
- else if (hash_algorithm_string == "SHA384")
- {
- result.key_encryptor.hash = hash_algorithm::sha384;
- }
-
- result.key_encryptor.salt_value = decode_base64(parser.attribute("saltValue"));
- result.key_encryptor.verifier_hash_input =
- decode_base64(parser.attribute("encryptedVerifierHashInput"));
- result.key_encryptor.verifier_hash_value =
- decode_base64(parser.attribute("encryptedVerifierHashValue"));
- result.key_encryptor.encrypted_key_value = decode_base64(parser.attribute("encryptedKeyValue"));
- }
- else
- {
- throw xlnt::unsupported("other encryption key types not supported");
- }
-
- parser.next_expect(xml::parser::event_type::end_element);
- }
-
- if (!any_password_key)
- {
- throw "no password key in keyEncryptors";
- }
-
- parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyEncryptor");
- parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyEncryptors");
-
- parser.next_expect(xml::parser::event_type::end_element, xmlns, "encryption");
-
- // begin key generation algorithm
-
- // H_0 = H(salt + password)
- auto salt_plus_password = result.key_encryptor.salt_value;
- std::vector password_wide(password.begin(), password.end());
-
- std::for_each(password_wide.begin(), password_wide.end(), [&salt_plus_password](std::uint16_t c) {
- salt_plus_password.insert(salt_plus_password.end(), reinterpret_cast(&c),
- reinterpret_cast(&c) + sizeof(std::uint16_t));
- });
-
- auto h_0 = hash(result.key_encryptor.hash, salt_plus_password);
-
- // H_n = H(iterator + H_n-1)
- std::vector iterator_plus_h_n(4, 0);
- iterator_plus_h_n.insert(iterator_plus_h_n.end(), h_0.begin(), h_0.end());
- std::uint32_t &iterator = *reinterpret_cast(iterator_plus_h_n.data());
- std::vector h_n;
-
- for (iterator = 0; iterator < result.key_encryptor.spin_count; ++iterator)
- {
- h_n = hash(result.key_encryptor.hash, iterator_plus_h_n);
- std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4);
- }
-
- static const std::size_t block_size = 8;
-
- auto calculate_block = [&result](const std::vector &raw_key,
- const std::array &block, const std::vector &encrypted) {
- auto combined = raw_key;
- combined.insert(combined.end(), block.begin(), block.end());
- auto key = hash(result.key_encryptor.hash, combined);
- key.resize(result.key_encryptor.key_bits / 8);
- return aes(
- key, result.key_encryptor.salt_value, encrypted, cipher_chaining::cbc, cipher_direction::decryption);
- };
-
- const std::array input_block_key = {{0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79}};
- auto hash_input = calculate_block(h_n, input_block_key, result.key_encryptor.verifier_hash_input);
- auto calculated_verifier = hash(result.key_encryptor.hash, hash_input);
-
- const std::array verifier_block_key = {
- {0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e}};
- auto expected_verifier = calculate_block(h_n, verifier_block_key, result.key_encryptor.verifier_hash_value);
- expected_verifier.resize(calculated_verifier.size());
-
- if (calculated_verifier.size() != expected_verifier.size()
- || std::mismatch(calculated_verifier.begin(), calculated_verifier.end(), expected_verifier.begin(),
- expected_verifier.end())
- != std::make_pair(calculated_verifier.end(), expected_verifier.end()))
- {
- throw xlnt::exception("bad password");
- }
-
- const std::array key_value_block_key = {
- {0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6}};
- auto key = calculate_block(h_n, key_value_block_key, result.key_encryptor.encrypted_key_value);
-
- auto salt_size = result.key_data.salt_size;
- auto salt_with_block_key = result.key_data.salt_value;
- salt_with_block_key.resize(salt_size + sizeof(std::uint32_t), 0);
-
- auto &segment = *reinterpret_cast(salt_with_block_key.data() + salt_size);
- auto total_size = static_cast(*reinterpret_cast(encrypted_package.data()));
-
- std::vector encrypted_segment(segment_length, 0);
- std::vector decrypted_package;
- decrypted_package.reserve(encrypted_package.size() - 8);
-
- for (std::size_t i = 8; i < encrypted_package.size(); i += segment_length)
- {
- auto iv = hash(result.key_encryptor.hash, salt_with_block_key);
- iv.resize(16);
-
- auto segment_begin = encrypted_package.begin() + static_cast(i);
- auto current_segment_length = std::min(segment_length, encrypted_package.size() - i);
- auto segment_end = encrypted_package.begin() + static_cast(i + current_segment_length);
- encrypted_segment.assign(segment_begin, segment_end);
- auto decrypted_segment =
- aes(key, iv, encrypted_segment, cipher_chaining::cbc, cipher_direction::decryption);
- decrypted_segment.resize(current_segment_length);
-
- decrypted_package.insert(decrypted_package.end(), decrypted_segment.begin(), decrypted_segment.end());
-
- ++segment;
- }
-
- decrypted_package.resize(total_size);
-
- return decrypted_package;
+ throw xlnt::exception("empty file");
}
- static std::vector decrypt_xlsx(const std::vector &bytes, const std::string &password)
+ std::vector as_chars(bytes.begin(), bytes.end());
+ POLE::Storage storage(as_chars.data(), static_cast(bytes.size()));
+
+ if (!storage.open())
{
- if (bytes.empty())
- {
- throw xlnt::exception("empty file");
- }
+ throw xlnt::exception("not an ole compound file");
+ }
- std::vector as_chars(bytes.begin(), bytes.end());
- POLE::Storage storage(as_chars.data(), static_cast(bytes.size()));
+ auto encrypted_package = file(storage, "EncryptedPackage");
+ auto encryption_info = file(storage, "EncryptionInfo");
- if (!storage.open())
- {
- throw xlnt::exception("not an ole compound file");
- }
+ std::size_t index = 0;
- auto encrypted_package = file(storage, "EncryptedPackage");
- auto encryption_info = file(storage, "EncryptionInfo");
+ auto version_major = read_int(index, encryption_info);
+ auto version_minor = read_int(index, encryption_info);
+ auto encryption_flags = read_int(index, encryption_info);
- std::size_t index = 0;
+ // get rid of header
+ encryption_info.erase(encryption_info.begin(), encryption_info.begin() + static_cast(index));
- auto version_major = read_int(index, encryption_info);
- auto version_minor = read_int(index, encryption_info);
- auto encryption_flags = read_int(index, encryption_info);
-
- // get rid of header
- encryption_info.erase(encryption_info.begin(), encryption_info.begin() + static_cast(index));
-
- // version 4.4 is agile
- if (version_major == 4 && version_minor == 4)
- {
- if (encryption_flags != 0x40)
- {
- throw xlnt::exception("bad header");
- }
-
- return decrypt_xlsx_agile(encryption_info, password, encrypted_package);
- }
-
- // not agile, only try to decrypt versions 3.2 and 4.2
- if (version_minor != 2 || (version_major != 2 && version_major != 3 && version_major != 4))
- {
- throw xlnt::exception("unsupported encryption version");
- }
-
- if ((encryption_flags & 0b00000011) != 0) // Reserved1 and Reserved2, MUST be 0
+ // version 4.4 is agile
+ if (version_major == 4 && version_minor == 4)
+ {
+ if (encryption_flags != 0x40)
{
throw xlnt::exception("bad header");
}
- if ((encryption_flags & 0b00000100) == 0 // fCryptoAPI
- || (encryption_flags & 0b00010000) != 0) // fExternal
- {
- throw xlnt::exception("extensible encryption is not supported");
- }
-
- if ((encryption_flags & 0b00100000) == 0) // fAES
- {
- throw xlnt::exception("not an OOXML document");
- }
-
- return decrypt_xlsx_standard(encryption_info, password, encrypted_package);
+ return decrypt_xlsx_agile(encryption_info, password, encrypted_package);
}
- static std::vector encrypt_xlsx(const std::vector &bytes, const std::string &password)
+ // not agile, only try to decrypt versions 3.2 and 4.2
+ if (version_minor != 2 || (version_major != 2 && version_major != 3 && version_major != 4))
{
- if (bytes.empty())
- {
- throw xlnt::exception("empty file");
- }
-
- generate_agile_encryption_info(password);
-
- return {};
+ throw xlnt::exception("unsupported encryption version");
}
-};
+
+ if ((encryption_flags & 0b00000011) != 0) // Reserved1 and Reserved2, MUST be 0
+ {
+ throw xlnt::exception("bad header");
+ }
+
+ if ((encryption_flags & 0b00000100) == 0 // fCryptoAPI
+ || (encryption_flags & 0b00010000) != 0) // fExternal
+ {
+ throw xlnt::exception("extensible encryption is not supported");
+ }
+
+ if ((encryption_flags & 0b00100000) == 0) // fAES
+ {
+ throw xlnt::exception("not an OOXML document");
+ }
+
+ return decrypt_xlsx_standard(encryption_info, password, encrypted_package);
+}
+
+std::vector crypto_helper::encrypt_xlsx(
+ const std::vector &bytes, const std::string &password)
+{
+ if (bytes.empty())
+ {
+ throw xlnt::exception("empty file");
+ }
+
+ generate_agile_encryption_info(password);
+
+ return {};
+}
const std::size_t crypto_helper::segment_length = 4096;
diff --git a/source/detail/xlsx_crypto.hpp b/source/detail/xlsx_crypto.hpp
new file mode 100644
index 00000000..1362f5d8
--- /dev/null
+++ b/source/detail/xlsx_crypto.hpp
@@ -0,0 +1,162 @@
+// Copyright (c) 2014-2017 Thomas Fussell
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE
+//
+// @license: http://www.opensource.org/licenses/mit-license.php
+// @author: see AUTHORS file
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include