mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
finally figured out how to decrypt with botan--swapping back out for botan
This commit is contained in:
parent
bc24515104
commit
9c8cba9f3f
7
.gitmodules
vendored
7
.gitmodules
vendored
|
@ -23,7 +23,6 @@
|
|||
url = https://github.com/catlan/pole
|
||||
branch = master
|
||||
|
||||
[submodule "third-party/nss"]
|
||||
path = third-party/nss
|
||||
url = https://github.com/tfussell/nss
|
||||
branch = master
|
||||
[submodule "third-party/botan"]
|
||||
path = third-party/botan
|
||||
url = https://github.com/randombit/botan
|
||||
|
|
|
@ -9,7 +9,6 @@ option(COVERAGE "Generate coverage data for use in Coveralls" OFF)
|
|||
option(WITH_EXAMPLES "Build examples" OFF)
|
||||
option(WITH_TESTS "Build tests" OFF)
|
||||
option(WITH_BENCHMARKS "Build performance benchmarks" OFF)
|
||||
option(WITH_CRYPTO "Set to ON to be able to read and write password-protected workbooks; requires botan and pole libraries" OFF)
|
||||
|
||||
if(APPLE)
|
||||
option(FRAMEWORK "Set to ON to package dylib and headers into a .framework, OSX only" OFF)
|
||||
|
|
|
@ -74,8 +74,8 @@ xlnt uses the following libraries, which are included in the source tree (all bu
|
|||
- [utfcpp v2.3.4](http://utfcpp.sourceforge.net/) (Boost Software License, Version 1.0)
|
||||
- [cxxtest v4.4](http://cxxtest.com/) (LGPLv3 license [only used for testing, separate from main library assembly])
|
||||
- [pugixml v1.7](http://cxxtest.com/) (LGPLv3 license [only used for testing, separate from main library assembly])
|
||||
- [botan v1.11](https://botan.randombit.net/) (BSD license [only used for reading/writing password-protected workbooks, WITH_CRYPTO=1])
|
||||
- [pole v0.5](https://github.com/catlan/pole) (BSD license [only used for reading/writing password-protected workbooks, WITH_CRYPTO=1])
|
||||
- [botan v1.11](https://botan.randombit.net/) (BSD license)
|
||||
- [pole v0.5](https://github.com/catlan/pole) (BSD license)
|
||||
|
||||
Initialize the submodules with this command:
|
||||
```bash
|
||||
|
|
3
cmake/build-botan-amalgamation
Executable file
3
cmake/build-botan-amalgamation
Executable file
|
@ -0,0 +1,3 @@
|
|||
cd ${0%/*}
|
||||
cd ../third-party/botan
|
||||
python configure.py --minimized-build --enable-modules=sha1,aes,filters,codec_filt,cbc,ecb,sha2_32,sha2_64 --disable-shared --amalgamation
|
|
@ -1,3 +1,2 @@
|
|||
cd ..
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
|
|
@ -24,10 +24,6 @@ if(NOT BIN_DEST_DIR)
|
|||
set(BIN_DEST_DIR ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
endif()
|
||||
|
||||
if(WITH_CRYPTO)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCRYPTO_ENABLED")
|
||||
endif()
|
||||
|
||||
include_directories(include)
|
||||
include_directories(include/xlnt)
|
||||
include_directories(source)
|
||||
|
@ -36,6 +32,7 @@ include_directories(third-party/miniz)
|
|||
include_directories(third-party/libstudxml)
|
||||
include_directories(third-party/utfcpp/source)
|
||||
include_directories(third-party/pole)
|
||||
include_directories(third-party/botan)
|
||||
|
||||
FILE(GLOB ROOT_HEADERS include/xlnt/*.hpp)
|
||||
FILE(GLOB CELL_HEADERS include/xlnt/cell/*.hpp)
|
||||
|
@ -70,13 +67,15 @@ set(SOURCES ${CELL_SOURCES} ${CHARTS_SOURCES} ${CHARTSHEET_SOURCES} ${DRAWING_SO
|
|||
|
||||
set(MINIZ ../third-party/miniz/miniz.c ../third-party/miniz/miniz.h)
|
||||
set(LIBSTUDXML ../third-party/libstudxml/xml/parser.cxx ../third-party/libstudxml/xml/qname.cxx ../third-party/libstudxml/xml/serializer.cxx ../third-party/libstudxml/xml/value-traits.cxx ../third-party/libstudxml/xml/details/expat/xmlparse.c ../third-party/libstudxml/xml/details/expat/xmlrole.c ../third-party/libstudxml/xml/details/expat/xmltok_impl.c ../third-party/libstudxml/xml/details/expat/xmltok_ns.c ../third-party/libstudxml/xml/details/expat/xmltok.c ../third-party/libstudxml/xml/details/genx/char-props.c ../third-party/libstudxml/xml/details/genx/genx.c)
|
||||
set(POLE ../third-party/pole/pole.cpp)
|
||||
set(BOTAN ../third-party/botan/botan_all.cpp)
|
||||
|
||||
if(WITH_CRYPTO)
|
||||
set(POLE ../third-party/pole/pole.cpp)
|
||||
endif()
|
||||
add_custom_command(OUTPUT ${BOTAN}
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/build-botan-amalgamation ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating botan amalgamation ${BOTAN}")
|
||||
|
||||
if(SHARED)
|
||||
add_library(xlnt.shared SHARED ${HEADERS} ${SOURCES} ${MINIZ} ${LIBSTUDXML} ${POLE})
|
||||
add_library(xlnt.shared SHARED ${HEADERS} ${SOURCES} ${MINIZ} ${LIBSTUDXML} ${POLE} ${BOTAN})
|
||||
target_compile_definitions(xlnt.shared PRIVATE XLNT_SHARED=1 LIBSTUDXML_STATIC_LIB=1)
|
||||
if(MSVC)
|
||||
target_compile_definitions(xlnt.shared PRIVATE XLNT_EXPORT=1 _CRT_SECURE_NO_WARNINGS=1)
|
||||
|
@ -115,7 +114,7 @@ if(SHARED)
|
|||
endif()
|
||||
|
||||
if(STATIC)
|
||||
add_library(xlnt.static STATIC ${HEADERS} ${SOURCES} ${MINIZ} ${LIBSTUDXML} ${POLE})
|
||||
add_library(xlnt.static STATIC ${HEADERS} ${SOURCES} ${MINIZ} ${LIBSTUDXML} ${POLE} ${BOTAN})
|
||||
target_compile_definitions(xlnt.static PUBLIC XLNT_STATIC=1)
|
||||
target_compile_definitions(xlnt.static PRIVATE LIBSTUDXML_STATIC_LIB=1)
|
||||
if(MSVC)
|
||||
|
|
|
@ -7,8 +7,8 @@ include_directories(third-party/cxxtest)
|
|||
include_directories(third-party/pugixml/src)
|
||||
|
||||
if(WITH_CRYPTO)
|
||||
include_directories(third-party/nss/nspr/lib/ds third-party/nss/nspr/lib/libc/include third-party/nss/nspr/pr/include third-party/nss/nss/lib/base third-party/nss/nss/lib/certdb third-party/nss/nss/lib/certhigh third-party/nss/nss/lib/cryptohi third-party/nss/nss/lib/nss third-party/nss/nss/lib/pk11wrap third-party/nss/nss/lib/pkcs7 third-party/nss/nss/lib/smime third-party/nss/nss/lib/util)
|
||||
link_directories(third-party/nss/build/lib)
|
||||
include_directories(third-party/botan/build/include)
|
||||
link_directories(third-party/botan/build/lib)
|
||||
endif()
|
||||
|
||||
FILE(GLOB CELL_TESTS source/cell/tests/test_*.hpp)
|
||||
|
@ -59,18 +59,14 @@ endif()
|
|||
|
||||
if(MSVC)
|
||||
set_target_properties(xlnt.test PROPERTIES COMPILE_FLAGS "/wd\"4251\" /wd\"4275\"")
|
||||
# Needed for PathFileExists in path_helper
|
||||
# Needed for PathFileExists in path_helper
|
||||
target_link_libraries(xlnt.test Shlwapi)
|
||||
if(WITH_CRYPTO)
|
||||
target_link_libraries(xlnt.test Ws2_32.lib Winmm.lib nss.lib)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_custom_command(OUTPUT ${RUNNER}
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate-tests ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS ${TESTS}
|
||||
COMMENT "Generating test runner ${RUNNER}"
|
||||
)
|
||||
COMMENT "Generating test runner ${RUNNER}")
|
||||
|
||||
add_custom_target(generate-test-runner DEPENDS ${RUNNER})
|
||||
|
||||
|
|
|
@ -355,22 +355,20 @@ public:
|
|||
void save(const xlnt::path &filename) const;
|
||||
void save(std::ostream &stream) const;
|
||||
|
||||
void load(const std::vector<std::uint8_t> &data);
|
||||
void load(const std::string &filename);
|
||||
void load(const xlnt::path &filename);
|
||||
void load(std::istream &stream);
|
||||
|
||||
#ifdef CRYPTO_ENABLED
|
||||
void save(const std::string &filename, const std::string &password);
|
||||
void save(const xlnt::path &filename, const std::string &password);
|
||||
void save(std::istream &stream, const std::string &password);
|
||||
void save(const std::vector<std::uint8_t> &data, const std::string &password);
|
||||
|
||||
void load(const std::vector<std::uint8_t> &data);
|
||||
void load(const std::string &filename);
|
||||
void load(const xlnt::path &filename);
|
||||
void load(std::istream &stream);
|
||||
|
||||
void load(const std::string &filename, const std::string &password);
|
||||
void load(const xlnt::path &filename, const std::string &password);
|
||||
void load(std::istream &stream, const std::string &password);
|
||||
void load(const std::vector<std::uint8_t> &data, const std::string &password);
|
||||
#endif
|
||||
|
||||
bool has_view() const;
|
||||
workbook_view get_view() const;
|
||||
|
|
|
@ -1,3 +1,25 @@
|
|||
// Copyright (c) 2014-2016 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 <cctype>
|
||||
|
||||
#include <detail/xlsx_consumer.hpp>
|
||||
|
@ -464,25 +486,31 @@ void xlsx_consumer::read_core_properties()
|
|||
if (parser().peek() == xml::parser::event_type::end_element) break;
|
||||
|
||||
parser().next_expect(xml::parser::event_type::start_element);
|
||||
parser().next_expect(xml::parser::event_type::characters);
|
||||
|
||||
std::string characters;
|
||||
if (parser().peek() == xml::parser::event_type::characters)
|
||||
{
|
||||
parser().next_expect(xml::parser::event_type::characters);
|
||||
characters = parser().value();
|
||||
}
|
||||
|
||||
if (parser().namespace_() == xmlns_dc && parser().name() == "creator")
|
||||
{
|
||||
target_.set_creator(parser().value());
|
||||
target_.set_creator(characters);
|
||||
}
|
||||
else if (parser().namespace_() == xmlns_cp && parser().name() == "lastModifiedBy")
|
||||
{
|
||||
target_.set_last_modified_by(parser().value());
|
||||
target_.set_last_modified_by(characters);
|
||||
}
|
||||
else if (parser().namespace_() == xmlns_dcterms && parser().name() == "created")
|
||||
{
|
||||
parser().attribute(xml::qname(xmlns_xsi, "type"));
|
||||
target_.set_created(w3cdtf_to_datetime(parser().value()));
|
||||
target_.set_created(w3cdtf_to_datetime(characters));
|
||||
}
|
||||
else if (parser().namespace_() == xmlns_dcterms && parser().name() == "modified")
|
||||
{
|
||||
parser().attribute(xml::qname(xmlns_xsi, "type"));
|
||||
target_.set_modified(w3cdtf_to_datetime(parser().value()));
|
||||
target_.set_modified(w3cdtf_to_datetime(characters));
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element);
|
||||
|
@ -535,11 +563,20 @@ void xlsx_consumer::read_workbook()
|
|||
{
|
||||
target_.d_->has_file_version_ = true;
|
||||
target_.d_->file_version_.app_name = parser().attribute("appName");
|
||||
target_.d_->file_version_.last_edited = string_to_size_t(parser().attribute("lastEdited"));
|
||||
target_.d_->file_version_.lowest_edited = string_to_size_t(parser().attribute("lowestEdited"));
|
||||
target_.d_->file_version_.rup_build = string_to_size_t(parser().attribute("rupBuild"));
|
||||
if (parser().attribute_present("lastEdited"))
|
||||
{
|
||||
target_.d_->file_version_.last_edited = string_to_size_t(parser().attribute("lastEdited"));
|
||||
}
|
||||
if (parser().attribute_present("lowestEdited"))
|
||||
{
|
||||
target_.d_->file_version_.lowest_edited = string_to_size_t(parser().attribute("lowestEdited"));
|
||||
}
|
||||
if (parser().attribute_present("lowestEdited"))
|
||||
{
|
||||
target_.d_->file_version_.rup_build = string_to_size_t(parser().attribute("rupBuild"));
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "fileVersion");
|
||||
parser().next_expect(xml::parser::event_type::end_element);
|
||||
}
|
||||
else if (qname == xml::qname(xmlns_mc, "AlternateContent"))
|
||||
{
|
||||
|
@ -563,10 +600,37 @@ void xlsx_consumer::read_workbook()
|
|||
view.y_window = string_to_size_t(parser().attribute("yWindow"));
|
||||
view.window_width = string_to_size_t(parser().attribute("windowWidth"));
|
||||
view.window_height = string_to_size_t(parser().attribute("windowHeight"));
|
||||
|
||||
if (parser().attribute_present("tabRatio"))
|
||||
{
|
||||
view.tab_ratio = string_to_size_t(parser().attribute("tabRatio"));
|
||||
}
|
||||
|
||||
if (parser().attribute_present("activeTab"))
|
||||
{
|
||||
parser().attribute("activeTab");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("firstSheet"))
|
||||
{
|
||||
parser().attribute("firstSheet");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("showHorizontalScroll"))
|
||||
{
|
||||
parser().attribute("showHorizontalScroll");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("showSheetTabs"))
|
||||
{
|
||||
parser().attribute("showSheetTabs");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("showVerticalScroll"))
|
||||
{
|
||||
parser().attribute("showVerticalScroll");
|
||||
}
|
||||
|
||||
target_.set_view(view);
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "workbookView");
|
||||
|
@ -593,6 +657,17 @@ void xlsx_consumer::read_workbook()
|
|||
parser().attribute("defaultThemeVersion");
|
||||
}
|
||||
|
||||
// todo: turn these structures into a method like skip_attribute(string name, bool optional)
|
||||
if (parser().attribute_present("backupFile"))
|
||||
{
|
||||
parser().attribute("backupFile");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("showObjects"))
|
||||
{
|
||||
parser().attribute("showObjects");
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "workbookPr");
|
||||
}
|
||||
else if (qname == xml::qname(xmlns, "sheets"))
|
||||
|
@ -613,6 +688,11 @@ void xlsx_consumer::read_workbook()
|
|||
sheet_title_index_map_[title] = index++;
|
||||
target_.d_->sheet_title_rel_id_map_[title] = rel_id;
|
||||
|
||||
if (parser().attribute_present("state"))
|
||||
{
|
||||
parser().attribute("state");
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns_s, "sheet");
|
||||
}
|
||||
|
||||
|
@ -621,11 +701,37 @@ void xlsx_consumer::read_workbook()
|
|||
else if (qname == xml::qname(xmlns, "calcPr"))
|
||||
{
|
||||
target_.d_->has_calculation_properties_ = true;
|
||||
parser().attribute("calcId");
|
||||
|
||||
if (parser().attribute_present("calcId"))
|
||||
{
|
||||
parser().attribute("calcId");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("concurrentCalc"))
|
||||
{
|
||||
parser().attribute("concurrentCalc");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("iterate"))
|
||||
{
|
||||
parser().attribute("iterate");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("iterateCount"))
|
||||
{
|
||||
parser().attribute("iterateCount");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("iterateDelta"))
|
||||
{
|
||||
parser().attribute("iterateDelta");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("refMode"))
|
||||
{
|
||||
parser().attribute("refMode");
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "calcPr");
|
||||
}
|
||||
else if (qname == xml::qname(xmlns, "extLst"))
|
||||
|
@ -650,12 +756,27 @@ void xlsx_consumer::read_workbook()
|
|||
parser().attribute("chartTrackingRefBase");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("stringRefSyntax"))
|
||||
{
|
||||
parser().attribute("stringRefSyntax");
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element);
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "ext");
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "extLst");
|
||||
}
|
||||
else if (qname == xml::qname(xmlns, "workbookProtection"))
|
||||
{
|
||||
while (parser().peek() != xml::parser::event_type::end_element
|
||||
|| parser().qname() != xml::qname(xmlns, "workbookProtection"))
|
||||
{
|
||||
parser().next();
|
||||
}
|
||||
|
||||
parser().next();
|
||||
}
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "workbook");
|
||||
|
@ -725,6 +846,8 @@ void xlsx_consumer::read_shared_string_table()
|
|||
|
||||
text t;
|
||||
|
||||
parser().attribute_map();
|
||||
|
||||
if (parser().name() == "t")
|
||||
{
|
||||
parser().next_expect(xml::parser::event_type::characters);
|
||||
|
@ -782,6 +905,7 @@ void xlsx_consumer::read_shared_string_table()
|
|||
|
||||
parser().next_expect(xml::parser::event_type::end_element);
|
||||
parser().next_expect(xml::parser::event_type::end_element);
|
||||
|
||||
strings.push_back(t);
|
||||
}
|
||||
|
||||
|
@ -849,6 +973,7 @@ void xlsx_consumer::read_stylesheet()
|
|||
std::string name;
|
||||
std::size_t record_id;
|
||||
std::size_t builtin_id;
|
||||
bool custom_builtin;
|
||||
};
|
||||
|
||||
std::vector<style_data> style_datas;
|
||||
|
@ -878,11 +1003,28 @@ void xlsx_consumer::read_stylesheet()
|
|||
parser().next_expect(xml::parser::event_type::start_element); // <border>
|
||||
parser().content(xml::parser::content_type::complex);
|
||||
|
||||
auto diagonal = diagonal_direction::neither;
|
||||
|
||||
if (parser().attribute_present("diagonalDown") && parser().attribute("diagonalDown") == "1")
|
||||
{
|
||||
diagonal = diagonal_direction::down;
|
||||
}
|
||||
|
||||
if (parser().attribute_present("diagonalUp") && parser().attribute("diagonalUp") == "1")
|
||||
{
|
||||
diagonal = diagonal == diagonal_direction::down ? diagonal_direction::both : diagonal_direction::up;
|
||||
}
|
||||
|
||||
if (diagonal != diagonal_direction::neither)
|
||||
{
|
||||
border.diagonal(diagonal);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (parser().peek() == xml::parser::event_type::end_element) break;
|
||||
|
||||
parser().next_expect(xml::parser::event_type::start_element);
|
||||
|
||||
auto side_type = xml::value_traits<xlnt::border_side>::parse(parser().name(), parser());
|
||||
xlnt::border::border_property side;
|
||||
|
||||
|
@ -1094,6 +1236,13 @@ void xlsx_consumer::read_stylesheet()
|
|||
new_font.underline(xlnt::font::underline_style::single);
|
||||
}
|
||||
}
|
||||
else if (parser().name() == "charset")
|
||||
{
|
||||
if (parser().attribute_present("val"))
|
||||
{
|
||||
parser().attribute("val");
|
||||
}
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element);
|
||||
}
|
||||
|
@ -1110,11 +1259,13 @@ void xlsx_consumer::read_stylesheet()
|
|||
{
|
||||
stylesheet.number_formats.clear();
|
||||
|
||||
auto count = parser().attribute<std::size_t>("count");
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (parser().peek() == xml::parser::event_type::end_element) break;
|
||||
parser().next_expect(xml::parser::event_type::start_element, xmlns, "numFmt");
|
||||
|
||||
parser().next_expect(xml::parser::event_type::start_element, "numFmt");
|
||||
auto format_string = parser().attribute("formatCode");
|
||||
|
||||
if (format_string == "GENERAL")
|
||||
|
@ -1128,10 +1279,24 @@ void xlsx_consumer::read_stylesheet()
|
|||
nf.set_id(string_to_size_t(parser().attribute("numFmtId")));
|
||||
|
||||
stylesheet.number_formats.push_back(nf);
|
||||
parser().next_expect(xml::parser::event_type::end_element); // numFmt
|
||||
}
|
||||
|
||||
if (count != stylesheet.number_formats.size())
|
||||
{
|
||||
throw xlnt::exception("counts don't match");
|
||||
}
|
||||
}
|
||||
else if (parser().qname() == xml::qname(xmlns, "colors"))
|
||||
{
|
||||
while (parser().peek() != xml::parser::event_type::end_element
|
||||
|| parser().qname() != xml::qname(xmlns, "colors"))
|
||||
{
|
||||
if (parser().next() == xml::parser::event_type::start_element)
|
||||
{
|
||||
parser().attribute_map();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parser().qname() == xml::qname(xmlns, "cellStyles"))
|
||||
{
|
||||
|
@ -1147,6 +1312,10 @@ void xlsx_consumer::read_stylesheet()
|
|||
data.name = parser().attribute("name");
|
||||
data.record_id = parser().attribute<std::size_t>("xfId");
|
||||
data.builtin_id = parser().attribute<std::size_t>("builtinId");
|
||||
if (parser().attribute_present("customBuiltin"))
|
||||
{
|
||||
data.custom_builtin = parser().attribute("customBuiltin") == "1";
|
||||
}
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "cellStyle");
|
||||
}
|
||||
|
||||
|
@ -1255,11 +1424,8 @@ void xlsx_consumer::read_stylesheet()
|
|||
}
|
||||
else if (parser().qname() == xml::qname(xmlns, "protection"))
|
||||
{
|
||||
parser().next_expect(xml::parser::event_type::start_element, "protection");
|
||||
record.protection.first.locked(is_true(parser().attribute("locked")));
|
||||
record.protection.first.hidden(is_true(parser().attribute("hidden")));
|
||||
parser().next_expect(xml::parser::event_type::end_element, "protection");
|
||||
|
||||
record.protection.second = !apply_protection_present || protection_applied;
|
||||
}
|
||||
|
||||
|
@ -1340,11 +1506,15 @@ void xlsx_consumer::read_stylesheet()
|
|||
parser().next_expect(xml::parser::event_type::end_namespace_decl);
|
||||
}
|
||||
}
|
||||
else if (parser().qname() == xml::qname(xmlns, "indexedColors"))
|
||||
{
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "indexedColors");
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element);
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "styleSheet");
|
||||
parser().next_expect(xml::parser::event_type::end_element);
|
||||
|
||||
auto lookup_number_format = [&](std::size_t number_format_id)
|
||||
{
|
||||
|
@ -1369,11 +1539,16 @@ void xlsx_consumer::read_stylesheet()
|
|||
return result;
|
||||
};
|
||||
|
||||
auto style_data_iter = style_datas.begin();
|
||||
std::size_t xf_id = 0;
|
||||
|
||||
for (const auto &record : style_records)
|
||||
{
|
||||
auto &new_style = stylesheet.create_style();
|
||||
auto style_data_iter = std::find_if(style_datas.begin(), style_datas.end(),
|
||||
[&xf_id](const style_data &s) { return s.record_id == xf_id; });
|
||||
++xf_id;
|
||||
|
||||
if (style_data_iter == style_datas.end()) continue;
|
||||
|
||||
new_style.name(style_data_iter->name);
|
||||
new_style.builtin_id(style_data_iter->builtin_id);
|
||||
|
@ -1384,8 +1559,6 @@ void xlsx_consumer::read_stylesheet()
|
|||
new_style.font(stylesheet.fonts.at(record.font_id.first), record.font_id.second);
|
||||
new_style.number_format(lookup_number_format(record.number_format_id.first), record.number_format_id.second);
|
||||
new_style.protection(record.protection.first, record.protection.second);
|
||||
|
||||
++style_data_iter;
|
||||
}
|
||||
|
||||
for (const auto &record : format_records)
|
||||
|
@ -1546,7 +1719,12 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
|
|||
|
||||
if (parser().attribute_present("customHeight"))
|
||||
{
|
||||
ws.get_row_properties(row_index).height = std::stold(parser().attribute("customHeight"));
|
||||
auto custom_height = parser().attribute("customHeight");
|
||||
|
||||
if (custom_height != "false")
|
||||
{
|
||||
ws.get_row_properties(row_index).height = std::stold(custom_height);
|
||||
}
|
||||
}
|
||||
|
||||
if (parser().attribute_present(xml::qname(xmlns_x14ac, "dyDescent")))
|
||||
|
@ -1554,21 +1732,19 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
|
|||
parser().attribute(xml::qname(xmlns_x14ac, "dyDescent"));
|
||||
}
|
||||
|
||||
std::string span_string = parser().attribute("spans");
|
||||
auto colon_index = span_string.find(':');
|
||||
auto min_column = full_range.get_top_left().get_column_index();
|
||||
auto max_column = full_range.get_bottom_right().get_column_index();
|
||||
|
||||
column_t min_column = 0;
|
||||
column_t max_column = 0;
|
||||
if (parser().attribute_present("spans"))
|
||||
{
|
||||
std::string span_string = parser().attribute("spans");
|
||||
auto colon_index = span_string.find(':');
|
||||
|
||||
if (colon_index != std::string::npos)
|
||||
{
|
||||
min_column = static_cast<column_t::index_t>(std::stoll(span_string.substr(0, colon_index)));
|
||||
max_column = static_cast<column_t::index_t>(std::stoll(span_string.substr(colon_index + 1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
min_column = full_range.get_top_left().get_column_index();
|
||||
max_column = full_range.get_bottom_right().get_column_index();
|
||||
if (colon_index != std::string::npos)
|
||||
{
|
||||
min_column = static_cast<column_t::index_t>(std::stoll(span_string.substr(0, colon_index)));
|
||||
max_column = static_cast<column_t::index_t>(std::stoll(span_string.substr(colon_index + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
if (parser().attribute_present("customFormat"))
|
||||
|
@ -1581,6 +1757,21 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
|
|||
parser().attribute("s");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("collapsed"))
|
||||
{
|
||||
parser().attribute("collapsed");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("hidden"))
|
||||
{
|
||||
parser().attribute("hidden");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("outlineLevel"))
|
||||
{
|
||||
parser().attribute("outlineLevel");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (parser().peek() == xml::parser::event_type::end_element) break;
|
||||
|
@ -1708,6 +1899,21 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
|
|||
parser().attribute("bestFit");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("collapsed"))
|
||||
{
|
||||
parser().attribute("collapsed");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("hidden"))
|
||||
{
|
||||
parser().attribute("hidden");
|
||||
}
|
||||
|
||||
if (parser().attribute_present("outlineLevel"))
|
||||
{
|
||||
parser().attribute("outlineLevel");
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "col");
|
||||
}
|
||||
|
||||
|
@ -1733,6 +1939,51 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
|
|||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "pageMargins");
|
||||
}
|
||||
else if (parser().qname() == xml::qname(xmlns, "pageSetUpPr"))
|
||||
{
|
||||
parser().attribute_map();
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "pageSetUpPr");
|
||||
}
|
||||
else if (parser().qname() == xml::qname(xmlns, "printOptions"))
|
||||
{
|
||||
parser().attribute_map();
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "printOptions");
|
||||
}
|
||||
else if (parser().qname() == xml::qname(xmlns, "pageSetup"))
|
||||
{
|
||||
parser().attribute_map();
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "pageSetup");
|
||||
}
|
||||
else if (parser().qname() == xml::qname(xmlns, "sheetPr"))
|
||||
{
|
||||
parser().attribute_map();
|
||||
|
||||
while (parser().peek() != xml::parser::event_type::end_element
|
||||
|| parser().qname() != xml::qname(xmlns, "sheetPr"))
|
||||
{
|
||||
if (parser().next() == xml::parser::event_type::start_element)
|
||||
{
|
||||
parser().attribute_map();
|
||||
}
|
||||
}
|
||||
|
||||
parser().next();
|
||||
}
|
||||
else if (parser().qname() == xml::qname(xmlns, "headerFooter"))
|
||||
{
|
||||
parser().attribute_map();
|
||||
|
||||
while (parser().peek() != xml::parser::event_type::end_element
|
||||
|| parser().qname() != xml::qname(xmlns, "headerFooter"))
|
||||
{
|
||||
if (parser().next() == xml::parser::event_type::start_element)
|
||||
{
|
||||
parser().attribute_map();
|
||||
}
|
||||
}
|
||||
|
||||
parser().next();
|
||||
}
|
||||
}
|
||||
|
||||
parser().next_expect(xml::parser::event_type::end_element, xmlns, "worksheet");
|
||||
|
|
|
@ -54,13 +54,11 @@ public:
|
|||
|
||||
void read(const std::vector<std::uint8_t> &source);
|
||||
|
||||
#ifdef CRYPTO_ENABLED
|
||||
void read(const path &source, const std::string &password);
|
||||
|
||||
void read(std::istream &source, const std::string &password);
|
||||
|
||||
void read(const std::vector<std::uint8_t> &source, const std::string &password);
|
||||
#endif
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
|
|
|
@ -1,11 +1,29 @@
|
|||
#ifdef CRYPTO_ENABLED
|
||||
|
||||
// Copyright (c) 2014-2016 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 <array>
|
||||
#include <pole.h>
|
||||
#include <botan_all.h>
|
||||
#include <include_libstudxml.hpp>
|
||||
#include <nss.h>
|
||||
#include <pk11pub.h>
|
||||
#include <sechash.h>
|
||||
|
||||
#include <detail/xlsx_consumer.hpp>
|
||||
#include <xlnt/utils/exceptions.hpp>
|
||||
|
@ -34,6 +52,12 @@ enum class cipher_chaining
|
|||
cfb // cipher feedback chaining
|
||||
};
|
||||
|
||||
enum class cipher_direction
|
||||
{
|
||||
encryption,
|
||||
decryption
|
||||
};
|
||||
|
||||
enum class hash_algorithm
|
||||
{
|
||||
sha1,
|
||||
|
@ -48,153 +72,43 @@ enum class hash_algorithm
|
|||
whirlpool
|
||||
};
|
||||
|
||||
std::vector<std::uint8_t> rijndael_ecb_decrypt(std::vector<std::uint8_t> key,
|
||||
const std::vector<std::uint8_t> &encrypted)
|
||||
std::vector<std::uint8_t> aes(const std::vector<std::uint8_t> &key,
|
||||
const std::vector<std::uint8_t> &iv,
|
||||
const std::vector<std::uint8_t> &encrypted,
|
||||
cipher_chaining chaining, cipher_direction direction)
|
||||
{
|
||||
static const CK_MECHANISM_TYPE mechanism = CKM_AES_ECB;
|
||||
static const CK_ATTRIBUTE_TYPE direction = CKA_DECRYPT;
|
||||
std::string cipher_name("AES-");
|
||||
cipher_name.append(std::to_string(key.size() * 8));
|
||||
cipher_name.append(chaining == cipher_chaining::ecb
|
||||
? "/ECB/NoPadding" : "/CBC/NoPadding");
|
||||
|
||||
// IV (null)
|
||||
auto nss_iv_param = PK11_ParamFromIV(mechanism, nullptr);
|
||||
auto botan_direction = direction == cipher_direction::decryption
|
||||
? Botan::DECRYPTION : Botan::ENCRYPTION;
|
||||
Botan::Pipe pipe(Botan::get_cipher(cipher_name, key, iv, botan_direction));
|
||||
pipe.process_msg(encrypted);
|
||||
auto decrypted = pipe.read_all();
|
||||
|
||||
// key
|
||||
SECItem nss_key_item{ siBuffer, key.data(), static_cast<unsigned int>(key.size()) };
|
||||
auto nss_key = PK11_ImportSymKey(PK11_GetBestSlot(mechanism, nullptr),
|
||||
mechanism, PK11_OriginUnwrap, direction, &nss_key_item, nullptr);
|
||||
|
||||
// context
|
||||
auto nss_context = PK11_CreateContextBySymKey(mechanism, direction, nss_key, nss_iv_param);
|
||||
|
||||
// decrypt
|
||||
std::vector<std::uint8_t> decrypted(encrypted.size(), 0);
|
||||
int output_length;
|
||||
PK11_CipherOp(nss_context, decrypted.data(), &output_length,
|
||||
static_cast<int>(encrypted.size()), encrypted.data(),
|
||||
static_cast<int>(encrypted.size()));
|
||||
|
||||
// clean up
|
||||
PK11_DestroyContext(nss_context, PR_TRUE);
|
||||
PK11_FreeSymKey(nss_key);
|
||||
SECITEM_FreeItem(nss_iv_param, PR_TRUE);
|
||||
|
||||
return decrypted;
|
||||
return std::vector<std::uint8_t>(decrypted.begin(), decrypted.end());
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> rijndael_cbc_decrypt(std::vector<std::uint8_t> key,
|
||||
std::vector<std::uint8_t> iv, const std::vector<std::uint8_t> &encrypted)
|
||||
{
|
||||
static const CK_MECHANISM_TYPE mechanism = CKM_AES_CBC;
|
||||
static const CK_ATTRIBUTE_TYPE direction = CKA_DECRYPT;
|
||||
|
||||
// IV
|
||||
SECItem nss_iv_item{ siBuffer, iv.data(),
|
||||
static_cast<unsigned int>(iv.size()) };
|
||||
auto nss_iv_param = PK11_ParamFromIV(mechanism, &nss_iv_item);
|
||||
|
||||
// key
|
||||
SECItem nss_key_item{ siBuffer, key.data(),
|
||||
static_cast<unsigned int>(key.size()) };
|
||||
auto nss_key = PK11_ImportSymKey(PK11_GetBestSlot(mechanism, nullptr),
|
||||
mechanism, PK11_OriginUnwrap, direction, &nss_key_item, nullptr);
|
||||
|
||||
// context
|
||||
auto nss_context = PK11_CreateContextBySymKey(mechanism, direction, nss_key, nss_iv_param);
|
||||
|
||||
// decrypt
|
||||
std::vector<std::uint8_t> decrypted(encrypted.size(), 0);
|
||||
int output_length;
|
||||
PK11_CipherOp(nss_context, decrypted.data(), &output_length,
|
||||
static_cast<int>(encrypted.size()), encrypted.data(),
|
||||
static_cast<int>(encrypted.size()));
|
||||
|
||||
// clean up
|
||||
PK11_DestroyContext(nss_context, PR_TRUE);
|
||||
PK11_FreeSymKey(nss_key);
|
||||
SECITEM_FreeItem(nss_iv_param, PR_TRUE);
|
||||
|
||||
return decrypted;
|
||||
};
|
||||
|
||||
// Adapted from https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64
|
||||
// This function is public domain
|
||||
std::vector<std::uint8_t> decode_base64(const std::string &encoded)
|
||||
{
|
||||
if (encoded.length() % 4)
|
||||
{
|
||||
throw xlnt::exception("invalid base64");
|
||||
}
|
||||
Botan::Pipe pipe(new Botan::Base64_Decoder);
|
||||
pipe.process_msg(encoded);
|
||||
auto decoded = pipe.read_all();
|
||||
|
||||
std::size_t padding = 0;
|
||||
return std::vector<std::uint8_t>(decoded.begin(), decoded.end());
|
||||
};
|
||||
|
||||
if (!encoded.empty())
|
||||
{
|
||||
if (encoded[encoded.length() - 1] == '=') padding++;
|
||||
if (encoded[encoded.length() - 2] == '=') padding++;
|
||||
}
|
||||
std::vector<std::uint8_t> hash(hash_algorithm algorithm,
|
||||
const std::vector<std::uint8_t> &input)
|
||||
{
|
||||
Botan::Pipe pipe(new Botan::Hash_Filter(
|
||||
algorithm == hash_algorithm::sha512 ? "SHA-512" : "SHA-1"));
|
||||
pipe.process_msg(input);
|
||||
auto hash = pipe.read_all();
|
||||
|
||||
std::vector<std::uint8_t> decoded(((encoded.length() / 4) * 3) - padding, 0);
|
||||
auto decoded_iter = decoded.begin();
|
||||
|
||||
std::uint32_t temp = 0;
|
||||
|
||||
for (auto encoded_iter = encoded.begin(); encoded_iter != encoded.end();)
|
||||
{
|
||||
for (std::size_t quantumPosition = 0; quantumPosition < 4; quantumPosition++)
|
||||
{
|
||||
auto current_char = *encoded_iter;
|
||||
temp <<= 6;
|
||||
|
||||
// convert character into index from 0 to 63
|
||||
if (current_char >= 'A' && current_char <= 'Z')
|
||||
{
|
||||
temp |= current_char - 'A';
|
||||
}
|
||||
else if (current_char >= 'a' && current_char <= 'z')
|
||||
{
|
||||
temp |= current_char - 71;
|
||||
}
|
||||
else if (current_char >= '0' && current_char <= '9')
|
||||
{
|
||||
temp |= current_char + 4;
|
||||
}
|
||||
else if (current_char == '+')
|
||||
{
|
||||
temp |= 62;
|
||||
}
|
||||
else if (current_char == '/')
|
||||
{
|
||||
temp |= 63;
|
||||
}
|
||||
else if (current_char == '=')
|
||||
{
|
||||
switch (encoded.end() - encoded_iter)
|
||||
{
|
||||
case 1: // one pad character
|
||||
*(decoded_iter++) = (temp >> 16) & 0x000000ff;
|
||||
*(decoded_iter++) = (temp >> 8) & 0x000000ff;
|
||||
return decoded;
|
||||
case 2: // two pad characters
|
||||
*(decoded_iter++) = (temp >> 10) & 0x000000ff;
|
||||
return decoded;
|
||||
default:
|
||||
throw std::runtime_error("Invalid Padding in Base 64!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Non-Valid Character in Base 64!");
|
||||
}
|
||||
|
||||
++encoded_iter;
|
||||
}
|
||||
|
||||
// split lower 24 bits into 3 bytes
|
||||
*(decoded_iter++) = (temp >> 16) & 0x000000FF;
|
||||
*(decoded_iter++) = (temp >> 8) & 0x000000FF;
|
||||
*(decoded_iter++) = (temp) & 0x000000FF;
|
||||
}
|
||||
|
||||
return decoded;
|
||||
return std::vector<std::uint8_t>(hash.begin(), hash.end());
|
||||
};
|
||||
|
||||
std::vector<std::uint8_t> get_file(POLE::Storage &storage, const std::string &name)
|
||||
|
@ -206,50 +120,12 @@ std::vector<std::uint8_t> get_file(POLE::Storage &storage, const std::string &na
|
|||
return bytes;
|
||||
}
|
||||
|
||||
template<typename InIter>
|
||||
std::vector<std::uint8_t> hash(hash_algorithm algorithm, InIter begin, InIter end)
|
||||
{
|
||||
HASH_HashType hash_type = HASH_HashType::HASH_AlgNULL;
|
||||
std::size_t out_length = 0;
|
||||
|
||||
if (algorithm == hash_algorithm::sha1)
|
||||
{
|
||||
hash_type = HASH_HashType::HASH_AlgSHA1;
|
||||
out_length = SHA1_LENGTH;
|
||||
}
|
||||
else if (algorithm == hash_algorithm::sha512)
|
||||
{
|
||||
hash_type = HASH_HashType::HASH_AlgSHA512;
|
||||
out_length = SHA512_LENGTH;
|
||||
}
|
||||
else if (algorithm == hash_algorithm::sha256)
|
||||
{
|
||||
hash_type = HASH_HashType::HASH_AlgSHA256;
|
||||
out_length = SHA256_LENGTH;
|
||||
}
|
||||
else if (algorithm == hash_algorithm::sha384)
|
||||
{
|
||||
hash_type = HASH_HashType::HASH_AlgSHA384;
|
||||
out_length = SHA384_LENGTH;
|
||||
}
|
||||
|
||||
auto context = HASH_Create(hash_type);
|
||||
HASH_Begin(context);
|
||||
std::vector<std::uint8_t> input(begin, end);
|
||||
HASH_Update(context, input.data(), static_cast<unsigned int>(input.size()));
|
||||
unsigned int write_length;
|
||||
std::vector<std::uint8_t> result(out_length, 0);
|
||||
HASH_End(context, result.data(), &write_length, static_cast<unsigned int>(out_length));
|
||||
HASH_Destroy(context);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto read_int(std::size_t &index, const std::vector<std::uint8_t> &raw_data)
|
||||
{
|
||||
auto result = *reinterpret_cast<const T *>(&raw_data[index]);
|
||||
index += sizeof(T);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
@ -351,8 +227,7 @@ std::vector<std::uint8_t> decrypt_xlsx_standard(const std::vector<std::uint8_t>
|
|||
reinterpret_cast<char *>(&c),
|
||||
reinterpret_cast<char *>(&c) + sizeof(std::uint16_t));
|
||||
});
|
||||
std::vector<std::uint8_t> h_0 = hash(info.hash,
|
||||
salt_plus_password.begin(), salt_plus_password.end());
|
||||
std::vector<std::uint8_t> h_0 = hash(info.hash, salt_plus_password);
|
||||
|
||||
// H_n = H(iterator + H_n-1)
|
||||
std::vector<std::uint8_t> iterator_plus_h_n(4, 0);
|
||||
|
@ -361,7 +236,7 @@ std::vector<std::uint8_t> decrypt_xlsx_standard(const std::vector<std::uint8_t>
|
|||
std::vector<std::uint8_t> h_n;
|
||||
for (iterator = 0; iterator < info.spin_count; ++iterator)
|
||||
{
|
||||
h_n = hash(info.hash, iterator_plus_h_n.begin(), iterator_plus_h_n.end());
|
||||
h_n = hash(info.hash, iterator_plus_h_n);
|
||||
std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4);
|
||||
}
|
||||
|
||||
|
@ -371,7 +246,7 @@ std::vector<std::uint8_t> decrypt_xlsx_standard(const std::vector<std::uint8_t>
|
|||
h_n_plus_block.insert(h_n_plus_block.end(),
|
||||
reinterpret_cast<const std::uint8_t *>(&block_number),
|
||||
reinterpret_cast<const std::uint8_t *>(&block_number) + sizeof(std::uint32_t));
|
||||
auto h_final = hash(info.hash, h_n_plus_block.begin(), h_n_plus_block.end());
|
||||
auto h_final = hash(info.hash, h_n_plus_block);
|
||||
|
||||
// X1 = H(h_final ^ 0x36)
|
||||
std::vector<std::uint8_t> buffer(64, 0x36);
|
||||
|
@ -379,7 +254,7 @@ std::vector<std::uint8_t> decrypt_xlsx_standard(const std::vector<std::uint8_t>
|
|||
{
|
||||
buffer[i] = static_cast<std::uint8_t>(0x36 ^ h_final[i]);
|
||||
}
|
||||
auto X1 = hash(info.hash, buffer.begin(), buffer.end());
|
||||
auto X1 = hash(info.hash, buffer);
|
||||
|
||||
// X2 = H(h_final ^ 0x5C)
|
||||
buffer.assign(64, 0x5c);
|
||||
|
@ -387,7 +262,7 @@ std::vector<std::uint8_t> decrypt_xlsx_standard(const std::vector<std::uint8_t>
|
|||
{
|
||||
buffer[i] = static_cast<std::uint8_t>(0x5c ^ h_final[i]);
|
||||
}
|
||||
auto X2 = hash(info.hash, buffer.begin(), buffer.end());
|
||||
auto X2 = hash(info.hash, buffer);
|
||||
|
||||
auto X3 = X1;
|
||||
X3.insert(X3.end(), X2.begin(), X2.end());
|
||||
|
@ -396,8 +271,9 @@ std::vector<std::uint8_t> decrypt_xlsx_standard(const std::vector<std::uint8_t>
|
|||
|
||||
//todo: verify here
|
||||
|
||||
std::vector<std::uint8_t> encrypted_data(encrypted_package.begin() + 8, encrypted_package.end());
|
||||
return rijndael_ecb_decrypt(key_derived, encrypted_data);
|
||||
return aes(key_derived, {}, std::vector<std::uint8_t>(
|
||||
encrypted_package.begin() + 8, encrypted_package.end()),
|
||||
cipher_chaining::ecb, cipher_direction::decryption);
|
||||
}
|
||||
|
||||
|
||||
|
@ -535,6 +411,7 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(const std::vector<std::uint8_t> &en
|
|||
// H_0 = H(salt + password)
|
||||
auto salt_plus_password = result.key_encryptor.salt_value;
|
||||
std::vector<std::uint16_t> password_wide(password.begin(), password.end());
|
||||
|
||||
std::for_each(password_wide.begin(), password_wide.end(),
|
||||
[&salt_plus_password](std::uint16_t c)
|
||||
{
|
||||
|
@ -542,18 +419,18 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(const std::vector<std::uint8_t> &en
|
|||
reinterpret_cast<char *>(&c),
|
||||
reinterpret_cast<char *>(&c) + sizeof(std::uint16_t));
|
||||
});
|
||||
std::vector<std::uint8_t> h_0 = hash(result.key_encryptor.hash_algorithm,
|
||||
salt_plus_password.begin(), salt_plus_password.end());
|
||||
|
||||
auto h_0 = hash(result.key_encryptor.hash_algorithm, salt_plus_password);
|
||||
|
||||
// H_n = H(iterator + H_n-1)
|
||||
std::vector<std::uint8_t> 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<std::uint32_t *>(iterator_plus_h_n.data());
|
||||
std::vector<std::uint8_t> h_n;
|
||||
|
||||
for (iterator = 0; iterator < result.key_encryptor.spin_count; ++iterator)
|
||||
{
|
||||
h_n = hash(result.key_encryptor.hash_algorithm,
|
||||
iterator_plus_h_n.begin(), iterator_plus_h_n.end());
|
||||
h_n = hash(result.key_encryptor.hash_algorithm, iterator_plus_h_n);
|
||||
std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4);
|
||||
}
|
||||
|
||||
|
@ -566,20 +443,20 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(const std::vector<std::uint8_t> &en
|
|||
{
|
||||
auto combined = raw_key;
|
||||
combined.insert(combined.end(), block.begin(), block.end());
|
||||
auto key = hash(result.key_encryptor.hash_algorithm, combined.begin(), combined.end());
|
||||
auto key = hash(result.key_encryptor.hash_algorithm, combined);
|
||||
key.resize(result.key_encryptor.key_bits / 8);
|
||||
return rijndael_cbc_decrypt(key, result.key_encryptor.salt_value, encrypted);
|
||||
return aes(key, result.key_encryptor.salt_value, encrypted,
|
||||
cipher_chaining::cbc, cipher_direction::decryption);
|
||||
};
|
||||
|
||||
const std::array<std::uint8_t, block_size> input_block_key
|
||||
= { 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79 };
|
||||
= { {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_algorithm,
|
||||
hash_input.begin(), hash_input.end());
|
||||
auto calculated_verifier = hash(result.key_encryptor.hash_algorithm, hash_input);
|
||||
|
||||
const std::array<std::uint8_t, block_size> verifier_block_key
|
||||
= { 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e };
|
||||
= { {0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e} };
|
||||
auto expected_verifier = calculate_block(h_n, verifier_block_key,
|
||||
result.key_encryptor.verifier_hash_value);
|
||||
|
||||
|
@ -592,7 +469,7 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(const std::vector<std::uint8_t> &en
|
|||
}
|
||||
|
||||
const std::array<std::uint8_t, block_size> key_value_block_key
|
||||
= { 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6 };
|
||||
= { {0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6} };
|
||||
auto key = calculate_block(h_n, key_value_block_key,
|
||||
result.key_encryptor.encrypted_key_value);
|
||||
|
||||
|
@ -609,12 +486,12 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(const std::vector<std::uint8_t> &en
|
|||
|
||||
for (std::size_t i = 8; i < encrypted_package.size(); i += segment_length)
|
||||
{
|
||||
auto iv = hash(result.key_encryptor.hash_algorithm,
|
||||
salt_with_block_key.begin(), salt_with_block_key.end());
|
||||
iv.resize(32);
|
||||
auto iv = hash(result.key_encryptor.hash_algorithm, salt_with_block_key);
|
||||
iv.resize(16);
|
||||
|
||||
auto decrypted_segment = rijndael_cbc_decrypt(key, iv, std::vector<std::uint8_t>(
|
||||
encrypted_package.begin() + i, encrypted_package.begin() + i + segment_length));
|
||||
auto decrypted_segment = aes(key, iv, std::vector<std::uint8_t>(
|
||||
encrypted_package.begin() + i, encrypted_package.begin() + i + segment_length),
|
||||
cipher_chaining::cbc, cipher_direction::decryption);
|
||||
decrypted_package.insert(decrypted_package.end(),
|
||||
decrypted_segment.begin(), decrypted_segment.end());
|
||||
|
||||
|
@ -628,15 +505,6 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(const std::vector<std::uint8_t> &en
|
|||
|
||||
std::vector<std::uint8_t> decrypt_xlsx(const std::vector<std::uint8_t> &bytes, const std::string &password)
|
||||
{
|
||||
// nss has checks for re-initialization, but there might be some overhead
|
||||
static bool nss_initialized = false;
|
||||
|
||||
if (!nss_initialized)
|
||||
{
|
||||
NSS_NoDB_Init(nullptr);
|
||||
nss_initialized = true;
|
||||
}
|
||||
|
||||
if (bytes.empty())
|
||||
{
|
||||
throw xlnt::exception("empty file");
|
||||
|
@ -720,5 +588,3 @@ void xlsx_consumer::read(const path &source, const std::string &password)
|
|||
|
||||
} // namespace detail
|
||||
} // namespace xlnt
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,25 @@
|
|||
// Copyright (c) 2014-2016 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 <cmath>
|
||||
#include <string>
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// Copyright (c) 2014-2016 Thomas Fussell
|
||||
// Copyright (c) 2010-2015 openpyxl
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -12,18 +12,26 @@ class test_consume_xlsx : public CxxTest::TestSuite
|
|||
public:
|
||||
void test_decrypt_agile()
|
||||
{
|
||||
#ifdef CRYPTO_ENABLED
|
||||
xlnt::workbook wb;
|
||||
// key is { 125, 177, 16, 188, 228, 63, 60, 108, 222, 145, 79, 49, 49, 13, 157, 98, 217, 33, 52, 3, 222, 124, 200, 242, 179, 57, 61, 97, 225, 195, 141, 242 };
|
||||
wb.load(path_helper::get_data_directory("14_encrypted_excel_2016.xlsx"), "secret");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_decrypt_libre_office()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
wb.load(path_helper::get_data_directory("15_encrypted_libre_office.xlsx"), "secret");
|
||||
}
|
||||
|
||||
void test_decrypt_standard()
|
||||
{
|
||||
#ifdef CRYPTO_ENABLED
|
||||
xlnt::workbook wb;
|
||||
wb.load(path_helper::get_data_directory("16_encrypted_excel_2007.xlsx"), "password");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_decrypt_numbers()
|
||||
{
|
||||
TS_SKIP("");
|
||||
xlnt::workbook wb;
|
||||
wb.load(path_helper::get_data_directory("17_encrypted_numbers.xlsx"), "secret");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -631,7 +631,6 @@ void workbook::load(const path &filename)
|
|||
consumer.read(filename);
|
||||
}
|
||||
|
||||
#ifdef CRYPTO_ENABLED
|
||||
void workbook::load(const std::string &filename, const std::string &password)
|
||||
{
|
||||
return load(path(filename), password);
|
||||
|
@ -656,7 +655,6 @@ void workbook::load(std::istream &stream, const std::string &password)
|
|||
detail::xlsx_consumer consumer(*this);
|
||||
consumer.read(stream, password);
|
||||
}
|
||||
#endif
|
||||
|
||||
void workbook::save(std::vector<std::uint8_t> &data) const
|
||||
{
|
||||
|
|
BIN
tests/data/17_encrypted_numbers.xlsx
Normal file
BIN
tests/data/17_encrypted_numbers.xlsx
Normal file
Binary file not shown.
1
third-party/botan
vendored
Submodule
1
third-party/botan
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 36e0ea1e407027ac48e82b56016a6813ff6a1082
|
1
third-party/nss
vendored
1
third-party/nss
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit 4cbd10b7e2f5a2b9179a316c5797c819e551e403
|
Loading…
Reference in New Issue
Block a user