From 89858e32b3c3926b09eea69d1b40af08dfaa55c4 Mon Sep 17 00:00:00 2001 From: Thomas Fussell Date: Sat, 22 Apr 2017 14:25:27 -0400 Subject: [PATCH] intermediate commit --- source/detail/bytes.hpp | 279 +++- .../detail/cryptography/compound_document.cpp | 1484 ++++++++--------- .../detail/cryptography/compound_document.hpp | 13 +- .../detail/cryptography/encryption_info.cpp | 4 +- .../cryptography/xlsx_crypto_consumer.cpp | 81 +- .../cryptography/xlsx_crypto_producer.cpp | 4 +- 6 files changed, 976 insertions(+), 889 deletions(-) diff --git a/source/detail/bytes.hpp b/source/detail/bytes.hpp index 82342186..e7d8052d 100644 --- a/source/detail/bytes.hpp +++ b/source/detail/bytes.hpp @@ -30,73 +30,230 @@ namespace xlnt { namespace detail { using byte = std::uint8_t; -using byte_vector = std::vector; -template -T read_int(const byte_vector &raw_data, std::size_t &index) +class byte_reader { - auto result = *reinterpret_cast(&raw_data[index]); - index += sizeof(T); +public: + byte_reader() = delete; - return result; -} - -template -void write_int(T value, byte_vector &raw_data, std::size_t &index) -{ - *reinterpret_cast(&raw_data[index]) = value; - index += sizeof(T); -} - -static inline void writeU16(std::uint8_t *ptr, std::uint16_t data) -{ - ptr[0] = static_cast(data & 0xff); - ptr[1] = static_cast((data >> 8) & 0xff); -} - -static inline void writeU32(std::uint8_t *ptr, std::uint32_t data) -{ - ptr[0] = static_cast(data & 0xff); - ptr[1] = static_cast((data >> 8) & 0xff); - ptr[2] = static_cast((data >> 16) & 0xff); - ptr[3] = static_cast((data >> 24) & 0xff); -} - - -template -byte *vector_byte(std::vector &v, std::size_t offset) -{ - return reinterpret_cast(v.data() + offset); -} - -template -byte *first_byte(std::vector &v) -{ - return vector_byte(v, 0); -} - -template -byte *last_byte(std::vector &v) -{ - return vector_byte(v, v.size()); -} - -template -byte_vector to_bytes(InIt begin, InIt end) -{ - byte_vector bytes; - - for (auto i = begin; i != end; ++i) + byte_reader(const std::vector &bytes) + : bytes_(&bytes) { - auto c = *i; - bytes.insert( - bytes.end(), - reinterpret_cast(&c), - reinterpret_cast(&c) + sizeof(c)); } - return bytes; -} + byte_reader &operator=(const byte_reader &other) + { + offset_ = other.offset_; + bytes_ = other.bytes_; + + return *this; + } + + ~byte_reader() + { + } + + const std::vector &data() const + { + return *bytes_; + } + + void offset(std::size_t offset) + { + offset_ = offset; + } + + std::size_t offset() const + { + return offset_; + } + + void reset() + { + offset_ = 0; + } + + template + T read() + { + T result; + std::memcpy(&result, bytes_->data() + offset_, sizeof(T)); + + return result; + } + + template + std::vector as_vector_of() const + { + auto result = std::vector(size() / sizeof(T), 0); + std::memcpy(result.data(), bytes_->data(), size()); + + return result; + } + + std::size_t size() const + { + return bytes_->size(); + } + +private: + std::size_t offset_ = 0; + const std::vector *bytes_; +}; + +class byte_vector +{ +public: + template + static byte_vector from(const std::vector &ints) + { + byte_vector result; + + result.resize(ints.size() / sizeof(T)); + std::memcpy(result.bytes_.data(), ints.data(), result.bytes_.size()); + + return result; + } + + template + static byte_vector from(const std::basic_string &string) + { + byte_vector result; + + result.resize(string.size() / sizeof(T)); + std::memcpy(result.bytes_.data(), string.data(), result.bytes_.size()); + + return result; + } + + byte_vector() + : reader_(bytes_) + { + } + + byte_vector(std::vector &bytes) + : bytes_(bytes), + reader_(bytes_) + { + } + + template + byte_vector(const std::vector &ints) + : byte_vector() + { + bytes_ = from(ints).data(); + } + + byte_vector(const byte_vector &other) + : byte_vector() + { + *this = other; + } + + ~byte_vector() + { + } + + byte_vector &operator=(const byte_vector &other) + { + bytes_ = other.bytes_; + reader_ = byte_reader(bytes_); + + return *this; + } + + const std::vector &data() const + { + return bytes_; + } + + std::vector data() + { + return bytes_; + } + + void data(std::vector &bytes) + { + bytes_ = bytes; + } + + void offset(std::size_t offset) + { + reader_.offset(offset); + } + + std::size_t offset() const + { + return reader_.offset(); + } + + void reset() + { + reader_.reset(); + bytes_.clear(); + } + + template + T read() + { + return reader_.read(); + } + + template + std::vector as_vector_of() const + { + return reader_.as_vector_of(); + } + + template + void write(T value) + { + const auto num_bytes = sizeof(T); + + if (offset() + num_bytes > size()) + { + extend(offset() + num_bytes - size()); + } + + std::memcpy(bytes_.data() + offset(), &value, num_bytes); + reader_.offset(reader_.offset() + num_bytes); + } + + std::size_t size() const + { + return bytes_.size(); + } + + void resize(std::size_t new_size, byte fill = 0) + { + bytes_.resize(new_size, fill); + } + + void extend(std::size_t amount, byte fill = 0) + { + bytes_.resize(size() + amount, fill); + } + + std::vector::iterator iterator() + { + return bytes_.begin() + offset(); + } + + void append(const std::vector &data, std::size_t offset, std::size_t count) + { + extend(count); + std::memcpy(bytes_.data(), data.data() + offset, count); + } + + void append(const std::vector &data) + { + append(data, 0, data.size()); + } + +private: + std::vector bytes_; + byte_reader reader_; +}; } // namespace detail } // namespace xlnt diff --git a/source/detail/cryptography/compound_document.cpp b/source/detail/cryptography/compound_document.cpp index d37a94a0..25252e57 100644 --- a/source/detail/cryptography/compound_document.cpp +++ b/source/detail/cryptography/compound_document.cpp @@ -31,844 +31,770 @@ #include #include +#include #include #include namespace { -struct header -{ - std::array id; // signature, or magic identifier - std::uint16_t big_shift; // bbat->blockSize = 1 << b_shift - std::uint16_t small_shift; // sbat->blockSize = 1 << s_shift - std::uint32_t num_big_blocks; // blocks allocated for big bat - std::uint32_t directory_start; // starting block for directory info - std::uint32_t threshold; // switch from small to big file (usually 4K) - std::uint32_t small_start; // starting block index to store small bat - std::uint32_t num_small_blocks; // blocks allocated for small bat - std::uint32_t meta_start; // starting block to store meta bat - std::uint32_t num_meta_blocks; // blocks allocated for meta bat - std::array bb_blocks; -}; +using xlnt::detail::byte_vector; + +using directory_id = std::int32_t; +using sector_id = std::int32_t; class allocation_table { public: - static const std::uint32_t Eof; - static const std::uint32_t Avail; - static const std::uint32_t Bat; - static const std::uint32_t MetaBat; - std::size_t blockSize; - allocation_table(); - void clear(); - std::size_t count(); - void resize(std::size_t newsize); - //void preserve(std::size_t n); - //void set(std::size_t index, std::uint32_t val); - //std::size_t unused(); - //void setChain(std::vector); - std::vector follow(std::uint32_t start); - void load(const std::vector &data); - //void save(std::vector &data); - //std::size_t size(); + static const sector_id FreeSector = -1; + static const sector_id EndOfChainSector = -2; + static const sector_id AllocationTableSector = -3; + static const sector_id MasterAllocationTableSector = -4; + + allocation_table::allocation_table() + { + resize(128); + } + + std::size_t allocation_table::count() const + { + return data_.size(); + } + + void allocation_table::resize(std::size_t newsize) + { + data_.resize(newsize, FreeSector); + } + + void allocation_table::set(std::size_t index, sector_id value) + { + if (index >= count()) resize(index + 1); + data_[index] = value; + } + + void allocation_table::setChain(std::vector chain) + { + if (chain.size()) + { + for (std::size_t i = 0; i < chain.size() - 1; i++) + { + set(chain[i], chain[i + 1]); + } + + set(chain[chain.size() - 1], allocation_table::EndOfChainSector); + } + } + + std::vector allocation_table::follow(sector_id start) const + { + auto chain = std::vector(); + if (start >= count()) return chain; + + auto p = start; + + auto already_exists = [](const std::vector &chain, sector_id item) + { + for (std::size_t i = 0; i < chain.size(); i++) + { + if (chain[i] == item) return true; + } + + return false; + }; + + while (p < count()) + { + if (p == static_cast(EndOfChainSector)) break; + if (p == static_cast(AllocationTableSector)) break; + if (p == static_cast(MasterAllocationTableSector)) break; + if (already_exists(chain, p)) break; + chain.push_back(p); + if (data_[p] >= count()) break; + p = data_[p]; + } + + return chain; + } + + void allocation_table::load(const byte_vector &data) + { + data_ = data.as_vector_of(); + } + + byte_vector allocation_table::save() const + { + return byte_vector::from(data_); + } + + std::size_t allocation_table::size_in_bytes() + { + return count() * 4; + } + + std::size_t sector_size() const + { + return sector_size_; + } + + void sector_size(std::size_t size) + { + sector_size_ = size; + } + private: - std::vector data_; + std::size_t sector_size_ = 4096; + std::vector data_; +}; + +class header +{ +public: + static const std::uint64_t magic; + + header() + { + } + + bool is_valid() const + { + if (threshold_ != 4096) return false; + if (num_sectors_ == 0) return false; + if ((num_sectors_ > 109) && (num_sectors_ > (num_master_sectors_ * 127) + 109)) return false; + if ((num_sectors_ < 109) && (num_master_sectors_ != 0)) return false; + if (short_sector_size_power_ > sector_size_power_) return false; + if (sector_size_power_ <= 6) return false; + if (sector_size_power_ >= 31) return false; + + return true; + } + + void load(byte_vector &data) + { + if (data.size() < 512) + { + throw xlnt::exception("bad header"); + } + + *this = data.read
(); + + if (file_id_ != 0xD0CF11E0A1B11AE1) + { + throw xlnt::exception("not ole"); + } + + if (!is_valid() || threshold_ != 4096) + { + throw xlnt::exception("bad ole"); + } + } + + byte_vector save() const + { + byte_vector out; + out.write(*this); + + return out; + } + + std::size_t sector_size() const + { + return std::size_t(1) << sector_size_power_; + } + + std::size_t short_sector_size() const + { + return std::size_t(1) << short_sector_size_power_; + } + + std::vector sectors() const + { + return std::vector(first_master_table.begin(), first_master_table.end()); + } + + std::size_t num_master_sectors() const + { + return static_cast(num_master_sectors_); + } + + sector_id master_table_start() const + { + return master_start_; + } + + sector_id short_table_start() const + { + return short_start_; + } + + sector_id directory_start() const + { + return directory_start_; + } + + std::size_t threshold() const + { + return threshold_; + } + +private: + std::uint64_t file_id_ = 0xD0CF11E0A1B11AE1; + std::array ignore1 = {{0}}; + std::uint16_t revision_ = 0x003E; + std::uint16_t version_ = 0x0003; + std::uint16_t byte_order_ = 0xFEFF; + std::uint16_t sector_size_power_ = 9; + std::uint16_t short_sector_size_power_ = 6; + std::array ignore2 = {{0}}; + std::uint32_t num_sectors_ = 0; + sector_id directory_start_ = 0; + std::array ignore3 = {{0}}; + std::uint32_t threshold_ = 4096; + sector_id short_start_ = 0; + std::uint32_t num_short_sectors_ = 0; + sector_id master_start_ = 0; + std::uint32_t num_master_sectors_ = 0; + std::array first_master_table = { allocation_table::FreeSector }; }; struct directory_entry { -public: - bool valid; // false if invalid (should be skipped) - std::string name; // the name, not in unicode anymore - bool dir; // true if directory - std::uint32_t size; // size (not valid if directory) - std::uint32_t start; // starting block - std::uint32_t prev; // previous sibling - std::uint32_t next; // next sibling - std::uint32_t child; // first child + std::array name = {{0}}; + std::uint16_t name_length = 0; + + enum entry_type + { + Empty = 0, + UserStorage = 1, + UserStream = 2, + LockBytes = 3, + Property = 4, + RootStorage = 5 + } type; + + enum entry_color + { + Red = 0, + Black = 1 + } color; + + directory_id prev = -1; + directory_id next = -1; + directory_id child = -1; + + std::array ignore1; + std::array ignore2; + + std::uint64_t created = 0; + std::uint64_t modified = 0; + + sector_id first = 0; + + std::uint32_t size = 0; + + std::array ignore3; + + bool operator==(const directory_entry &rhs) const + { + return name == rhs.name + && name_length == rhs.name_length + && type == rhs.type + && color == rhs.color + && prev == rhs.prev + && next == rhs.next + && child == rhs.child + && created == rhs.created + && modified == rhs.modified + && first == rhs.first + && size == rhs.size; + } }; class directory_tree { public: - static const std::uint32_t End; - directory_tree(); - void clear(); - std::size_t entryCount(); - directory_entry* entry(std::size_t index); - directory_entry* entry(const std::string& name, bool create = false); - std::ptrdiff_t indexOf(directory_entry* e); - std::ptrdiff_t parent(std::size_t index); - std::string fullName(std::size_t index); - std::vector children(std::size_t index); - void load(const std::vector &data); - //void save(std::vector &data); - //std::size_t size(); + static const directory_id End = -1; + + static void entry_name(directory_entry &entry, std::u16string name) + { + if (name.size() > 31) + { + name.resize(31); + } + + std::copy(name.begin(), name.end(), entry.name.begin()); + entry.name[name.size()] = 0; + entry.name_length = static_cast((name.size() + 1) * 2); + } + + directory_tree::directory_tree() + : entries() + { + clear(); + } + + void directory_tree::clear() + { + entries = { create_root_entry() }; + } + + std::size_t directory_tree::entry_count() + { + return entries.size(); + } + + directory_entry &entry(directory_id index) + { + return entries[index]; + } + + const directory_entry &entry(const std::u16string &name) const + { + return const_cast(*this).entry(name); + } + + directory_entry &entry(const std::u16string &name, bool create) + { + if (!name.length()) + { + throw xlnt::exception("bad name"); + } + + // quick check for "/" (that's root) + if (name == u"/") return entry(0); + + // split the names, e.g "/ObjectPool/_1020961869" will become: + // "ObjectPool" and "_1020961869" + auto names = std::vector(); + auto start = std::size_t(0); + auto end = std::size_t(0); + + if (name[0] == u'/') start++; + + while (start < name.length()) + { + end = name.find_first_of('/', start); + if (end == std::string::npos) end = name.length(); + names.push_back(name.substr(start, end - start)); + start = end + 1; + } + + // start from root + auto index = directory_id(0); + + for (auto it = names.begin(); it != names.end(); ++it) + { + // find among the children of index + auto chi = children(index); + std::ptrdiff_t child = 0; + + for (std::size_t i = 0; i < chi.size(); i++) + { + auto ce = entry(chi[i]); + + if (std::u16string(ce.name.data()) == *it) + { + child = static_cast(chi[i]); + } + } + + // traverse to the child + if (child > 0) + { + index = static_cast(child); + } + else + { + // not found among children + if (!create) + { + throw xlnt::exception("not found"); + } + + // create a new entry + auto parent = index; + entries.push_back(directory_entry()); + index = static_cast(entry_count() - 1); + auto &e = entry(index); + e.first = 0; + entry(parent).prev = index; + } + } + + return entry(index); + } + + directory_id parent(std::size_t index) + { + // brute-force, basically we iterate for each entries, find its children + // and check if one of the children is 'index' + for (auto j = directory_id(0); j < static_cast(entry_count()); j++) + { + auto chi = children(j); + + for (std::size_t i = 0; i < chi.size(); i++) + { + if (chi[i] == index) + { + return static_cast(j); + } + } + } + + return -1; + } + + std::u16string directory_tree::path(directory_id index) + { + // don't use root name ("Root Entry"), just give "/" + if (index == 0) return u"/"; + + auto current_entry = entry(index); + + auto result = std::u16string(entry(index).name.data()); + result.insert(0, u"/"); + + auto current_parent = parent(index); + + while (current_parent > 0) + { + current_entry = entry(current_parent); + + result.insert(0, std::u16string(current_entry.name.data())); + result.insert(0, u"/"); + + --current_parent; + index = current_parent; + + if (current_parent <= 0) break; + } + + return result; + } + + std::vector directory_tree::children(directory_id index) + { + auto result = std::vector(); + auto &e = entry(index); + + if (e.child < entry_count()) + { + find_siblings(result, e.child); + } + + return result; + } + + + void directory_tree::load(byte_vector &data) + { + entries.clear(); + + auto num_entries = data.size() / sizeof(directory_entry); + + for (auto i = std::size_t(0); i < num_entries; ++i) + { + auto e = data.read(); + + if ((e.type != directory_entry::UserStream) + && (e.type != directory_entry::UserStorage) + && (e.type != directory_entry::RootStorage)) + { + throw xlnt::exception("invalid entry"); + } + + entries.push_back(e); + } + } + + + byte_vector directory_tree::save() const + { + auto result = byte_vector(); + + for (auto &entry : entries) + { + result.write(entry); + } + + return result; + } + + // return space required to save this dirtree + std::size_t directory_tree::size() + { + return entry_count() * sizeof(directory_entry); + } + + directory_entry create_root_entry() const + { + directory_entry root; + + entry_name(root, u"Root Entry"); + root.type = directory_entry::RootStorage; + root.color = directory_entry::Black; + root.size = 0; + + return root; + } + private: + // helper function: recursively find siblings of index + void find_siblings(std::vector &result, directory_id index) + { + auto e = entry(index); + + // prevent infinite loop + for (std::size_t i = 0; i < result.size(); i++) + { + if (result[i] == index) return; + } + + // add myself + result.push_back(index); + + // visit previous sibling, don't go infinitely + auto prev = e.prev; + + if ((prev > 0) && (prev < entry_count())) + { + for (std::size_t i = 0; i < result.size(); i++) + { + if (result[i] == prev) + { + prev = 0; + } + } + + if (prev) + { + find_siblings(result, prev); + } + } + + // visit next sibling, don't go infinitely + auto next = e.next; + + if ((next > 0) && (next < entry_count())) + { + for (std::size_t i = 0; i < result.size(); i++) + { + if (result[i] == next) next = 0; + } + + if (next) + { + find_siblings(result, next); + } + } + } + std::vector entries; }; -const std::uint32_t allocation_table::Avail = 0xffffffff; -const std::uint32_t allocation_table::Eof = 0xfffffffe; -const std::uint32_t allocation_table::Bat = 0xfffffffd; -const std::uint32_t allocation_table::MetaBat = 0xfffffffc; - -allocation_table::allocation_table() - : blockSize(4096) -{ - // initial size - resize(128); -} - -std::size_t allocation_table::count() -{ - return data_.size(); -} - -void allocation_table::resize(std::size_t newsize) -{ - data_.resize(newsize, Avail); -} - -// make sure there're still free blocks -/* -void allocation_table::preserve(std::size_t n) -{ - std::vector pre; - for (std::size_t i = 0; i < n; i++) - pre.push_back(unused()); -} -*/ - -/* -void allocation_table::set(std::size_t index, std::uint32_t value) -{ - if (index >= count()) resize(index + 1); - data_[index] = value; -} -*/ - -/* -void allocation_table::setChain(std::vector chain) -{ - if (chain.size()) - { - for (std::size_t i = 0; i < chain.size() - 1; i++) - set(chain[i], chain[i + 1]); - set(chain[chain.size() - 1], allocation_table::Eof); - } -} -*/ - -// TODO: optimize this with better search -static bool already_exist(const std::vector &chain, std::uint32_t item) -{ - for (std::size_t i = 0; i < chain.size(); i++) - if (chain[i] == item) return true; - - return false; -} - -// follow -std::vector allocation_table::follow(std::uint32_t start) -{ - auto chain = std::vector(); - if (start >= count()) return chain; - - auto p = start; - - while (p < count()) - { - if (p == static_cast(Eof)) break; - if (p == static_cast(Bat)) break; - if (p == static_cast(MetaBat)) break; - if (already_exist(chain, p)) break; - chain.push_back(p); - if (data_[p] >= count()) break; - p = data_[p]; - } - - return chain; -} - -/* -std::size_t allocation_table::unused() -{ - // find first available block - for (std::size_t i = 0; i < data_.size(); i++) - if (data_[i] == Avail) return i; - - // completely full, so enlarge the table - std::size_t block = data_.size(); - resize(data_.size() + 10); - return block; -} -*/ - -void allocation_table::load(const std::vector &data) -{ - resize(data.size() / 4); - std::copy( - reinterpret_cast(&data[0]), - reinterpret_cast(&data[0] + data.size()), - data_.begin()); -} - -// return space required to save this dirtree -/* -std::size_t allocation_table::size() -{ - return count() * 4; -} -*/ - -/* -void allocation_table::save(std::vector &data) -{ - auto offset = std::size_t(0); - - for (std::size_t i = 0; i < count(); i++) - { - xlnt::detail::write_int(data_[i], data, offset); - } -} -*/ - -const std::uint32_t directory_tree::End = 0xffffffff; - -directory_tree::directory_tree() - : entries() -{ - clear(); -} - -void directory_tree::clear() -{ - // leave only root entry - entries.resize(1); - entries[0].valid = true; - entries[0].name = "Root Entry"; - entries[0].dir = true; - entries[0].size = 0; - entries[0].start = End; - entries[0].prev = End; - entries[0].next = End; - entries[0].child = End; -} - -std::size_t directory_tree::entryCount() -{ - return entries.size(); -} - -directory_entry *directory_tree::entry(std::size_t index) -{ - if (index >= entryCount()) return nullptr; - return &entries[index]; -} - -/* -std::ptrdiff_t directory_tree::indexOf(directory_entry *e) -{ - for (std::size_t i = 0; i < entryCount(); i++) - if (entry(i) == e) return static_cast(i); - - return -1; -} -*/ - -/* -std::ptrdiff_t directory_tree::parent(std::size_t index) -{ - // brute-force, basically we iterate for each entries, find its children - // and check if one of the children is 'index' - for (std::size_t j = 0; j < entryCount(); j++) - { - std::vector chi = children(j); - for (std::size_t i = 0; i < chi.size(); i++) - if (chi[i] == index) return static_cast(j); - } - - return -1; -} -*/ - -/* -std::string directory_tree::fullName(std::size_t index) -{ - // don't use root name ("Root Entry"), just give "/" - if (index == 0) return "/"; - - std::string result = entry(index)->name; - result.insert(0, "/"); - auto p = parent(index); - directory_entry *_entry = nullptr; - while (p > 0) - { - _entry = entry(static_cast(p)); - if (_entry->dir && _entry->valid) - { - result.insert(0, _entry->name); - result.insert(0, "/"); - } - --p; - index = static_cast(p); - if (p <= 0) break; - } - return result; -} -*/ - -// given a fullname (e.g "/ObjectPool/_1020961869"), find the entry -// if not found and create is false, return 0 -// if create is true, a new entry is returned -directory_entry *directory_tree::entry(const std::string &name, bool create) -{ - if (!name.length()) return nullptr; - - // quick check for "/" (that's root) - if (name == "/") return entry(0); - - // split the names, e.g "/ObjectPool/_1020961869" will become: - // "ObjectPool" and "_1020961869" - std::list names; - std::string::size_type start = 0, end = 0; - if (name[0] == '/') start++; - while (start < name.length()) - { - end = name.find_first_of('/', start); - if (end == std::string::npos) end = name.length(); - names.push_back(name.substr(start, end - start)); - start = end + 1; - } - - // start from root - std::size_t index = 0; - - // trace one by one - std::list::iterator it; - - for (it = names.begin(); it != names.end(); ++it) - { - // find among the children of index - std::vector chi = children(index); - std::ptrdiff_t child = 0; - for (std::size_t i = 0; i < chi.size(); i++) - { - directory_entry *ce = entry(chi[i]); - if (ce) - if (ce->valid && (ce->name.length() > 1)) - if (ce->name == *it) child = static_cast(chi[i]); - } - - // traverse to the child - if (child > 0) - index = static_cast(child); - else - { - // not found among children - if (!create) return nullptr; - - // create a new entry - std::size_t parent = index; - entries.push_back(directory_entry()); - index = entryCount() - 1; - directory_entry *e = entry(index); - e->valid = true; - e->name = *it; - e->dir = false; - e->size = 0; - e->start = 0; - e->child = End; - e->prev = End; - e->next = entry(parent)->child; - entry(parent)->child = static_cast(index); - } - } - - return entry(index); -} - -// helper function: recursively find siblings of index -void dirtree_find_siblings(directory_tree *dirtree, std::vector &result, std::size_t index) -{ - auto e = dirtree->entry(index); - if (!e) return; - if (!e->valid) return; - - // prevent infinite loop - for (std::size_t i = 0; i < result.size(); i++) - if (result[i] == index) return; - - // add myself - result.push_back(index); - - // visit previous sibling, don't go infinitely - std::size_t prev = e->prev; - if ((prev > 0) && (prev < dirtree->entryCount())) - { - for (std::size_t i = 0; i < result.size(); i++) - if (result[i] == prev) prev = 0; - if (prev) dirtree_find_siblings(dirtree, result, prev); - } - - // visit next sibling, don't go infinitely - std::size_t next = e->next; - if ((next > 0) && (next < dirtree->entryCount())) - { - for (std::size_t i = 0; i < result.size(); i++) - if (result[i] == next) next = 0; - if (next) dirtree_find_siblings(dirtree, result, next); - } -} - -std::vector directory_tree::children(std::size_t index) -{ - std::vector result; - - directory_entry *e = entry(index); - if (e) - if (e->valid && e->child < entryCount()) dirtree_find_siblings(this, result, e->child); - - return result; -} - -void directory_tree::load(const std::vector &data) -{ - entries.clear(); - - for (std::size_t i = 0; i < data.size() / 128; i++) - { - std::size_t p = i * 128; - auto offset = p + 0x40; - - // parse name of this entry, which stored as Unicode 16-bit - std::string name; - auto name_len = static_cast(xlnt::detail::read_int(data, offset)); - if (name_len > 64) name_len = 64; - for (std::size_t j = 0; (data[j + p]) && (j < name_len); j += 2) - { - name.append(1, static_cast(data[j + p])); - } - - // first char isn't printable ? remove it... - if (data[p] < 32) - { - name.erase(0, 1); - } - - // 2 = file (aka stream), 1 = directory (aka storage), 5 = root - std::size_t type = data[0x42 + p]; - - directory_entry e; - e.valid = true; - e.name = name; - offset = 0x74 + p; - e.start = xlnt::detail::read_int(data, offset); - offset = 0x78 + p; - e.size = xlnt::detail::read_int(data, offset); - offset = 0x44 + p; - e.prev = xlnt::detail::read_int(data, offset); - offset = 0x48 + p; - e.next = xlnt::detail::read_int(data, offset); - offset = 0x4C + p; - e.child = xlnt::detail::read_int(data, offset); - e.dir = (type != 2); - - // sanity checks - if ((type != 2) && (type != 1) && (type != 5)) e.valid = false; - if (name_len < 1) e.valid = false; - - entries.push_back(e); - } -} - -// return space required to save this dirtree -/* -std::size_t directory_tree::size() -{ - return entryCount() * 128; -} -*/ - -/* -void directory_tree::save(std::vector &data) -{ - std::fill(data.begin(), data.begin() + size(), std::uint8_t(0)); - - // root is fixed as "Root Entry" - directory_entry *root = entry(0); - std::string entry_name = "Root Entry"; - - for (std::size_t j = 0; j < entry_name.length(); j++) - { - data[j * 2] = static_cast(entry_name[j]); - } - - auto offset = std::size_t(0x40); - xlnt::detail::write_int(static_cast(entry_name.length() * 2 + 2), data, offset); - data[0x42] = 5; - data[0x43] = 1; - xlnt::detail::write_int(static_cast(0xffffffff), data, offset); - xlnt::detail::write_int(static_cast(0xffffffff), data, offset); - xlnt::detail::write_int(static_cast(root->child), data, offset); - - offset = 0x74; - xlnt::detail::write_int(0xffffffff, data, offset); - - for (std::size_t i = 1; i < entryCount(); i++) - { - directory_entry *e = entry(i); - if (!e) continue; - - if (e->dir) - { - e->start = 0xffffffff; - e->size = 0; - } - - // max length for name is 32 chars - entry_name = e->name; - if (entry_name.length() > 32) entry_name.erase(32, entry_name.length()); - - // write name as Unicode 16-bit - for (std::size_t j = 0; j < entry_name.length(); j++) - { - data[i * 128 + j * 2] = static_cast(entry_name[j]); - } - - offset = 0x40 + i * 128; - xlnt::detail::write_int(static_cast(entry_name.length() * 2 + 2), data, offset); - xlnt::detail::write_int(static_cast(0), data, offset); - xlnt::detail::write_int(e->prev, data, offset); - xlnt::detail::write_int(e->next, data, offset); - xlnt::detail::write_int(e->child, data, offset); - - offset = 0x74 + i * 128; - xlnt::detail::write_int(e->start, data, offset); - xlnt::detail::write_int(e->size, data, offset); - - data[i * 128 + 0x42] = e->dir ? 1 : 2; - data[i * 128 + 0x43] = 1; // always black - } -} -*/ - -static const std::array pole_magic = {0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1}; - -header initialize_header() -{ - header h; - - h.big_shift = 9; - h.small_shift = 6; - h.num_big_blocks = 0; - h.directory_start = 0; - h.threshold = 4096; - h.small_start = 0; - h.num_small_blocks = 0; - h.meta_start = 0; - h.num_meta_blocks = 0; - std::copy(pole_magic.begin(), pole_magic.end(), h.id.begin()); - h.bb_blocks.fill(allocation_table::Avail); - - return h; -} - -bool header_is_valid(const header &h) -{ - if (h.threshold != 4096) return false; - if (h.num_big_blocks == 0) return false; - if ((h.num_big_blocks > 109) && (h.num_big_blocks > (h.num_meta_blocks * 127) + 109)) return false; - if ((h.num_big_blocks < 109) && (h.num_meta_blocks != 0)) return false; - if (h.small_shift > h.big_shift) return false; - if (h.big_shift <= 6) return false; - if (h.big_shift >= 31) return false; - - return true; -} - -header load_header(const std::vector &data) -{ - header h; - - auto in = &data[0]; - auto offset = std::size_t(0x1e); - - h.big_shift = xlnt::detail::read_int(data, offset); - h.small_shift = xlnt::detail::read_int(data, offset); - - offset = 0x2c; - h.num_big_blocks = xlnt::detail::read_int(data, offset); - h.directory_start = xlnt::detail::read_int(data, offset); - - offset = 0x38; - h.threshold = xlnt::detail::read_int(data, offset); - h.small_start = xlnt::detail::read_int(data, offset); - h.num_small_blocks = xlnt::detail::read_int(data, offset); - h.meta_start = xlnt::detail::read_int(data, offset); - h.num_meta_blocks = xlnt::detail::read_int(data, offset); - - for (std::size_t i = 0; i < 8; i++) - { - h.id[i] = in[i]; - } - - offset = 0x4c; - - for (std::size_t i = 0; i < 109; i++) - { - h.bb_blocks[i] = xlnt::detail::read_int(data, offset); - } - - return h; -} - -/* -void save_header(const header &h, std::vector &out) -{ - std::fill(out.begin(), out.begin() + 0x4c, std::uint8_t(0)); - std::copy(h.id.begin(), h.id.end(), out.begin()); - - auto offset = std::size_t(0); - - xlnt::detail::write_int(static_cast(0), out, offset); - xlnt::detail::write_int(static_cast(0), out, offset); - xlnt::detail::write_int(static_cast(0), out, offset); - xlnt::detail::write_int(static_cast(0), out, offset); - xlnt::detail::write_int(static_cast(0x003e), out, offset); - xlnt::detail::write_int(static_cast(3), out, offset); - xlnt::detail::write_int(static_cast(0xfffe), out, offset); - - xlnt::detail::write_int(h.big_shift, out, offset); - xlnt::detail::write_int(h.small_shift, out, offset); - - offset = 0x2c; - - xlnt::detail::write_int(h.num_big_blocks, out, offset); - xlnt::detail::write_int(h.directory_start, out, offset); - xlnt::detail::write_int(static_cast(0), out, offset); - xlnt::detail::write_int(h.threshold, out, offset); - xlnt::detail::write_int(h.small_start, out, offset); - xlnt::detail::write_int(h.num_small_blocks, out, offset); - xlnt::detail::write_int(h.meta_start, out, offset); - xlnt::detail::write_int(h.num_meta_blocks, out, offset); - - for (std::size_t i = 0; i < 109; i++) - { - xlnt::detail::write_int(h.bb_blocks[i], out, offset); - } -} -*/ - } // namespace namespace xlnt { namespace detail { -struct compound_document_impl +class compound_document_impl { - byte_vector buffer_; +public: + compound_document_impl() + { + sector_table_.sector_size(header_.sector_size()); + short_sector_table_.sector_size(header_.short_sector_size()); + } + + byte_vector load_sectors(const std::vector §ors) const + { + auto result = byte_vector(); + const auto sector_size = sector_table_.sector_size(); + + for (auto sector : sectors) + { + auto position = sector_size * (sector + 1); + result.append(sectors_.data(), position, sector_size); + } + + return result; + } + + byte_vector load_short_sectors(const std::vector §ors) const + { + auto result = byte_vector(); + const auto short_sector_size = short_sector_table_.sector_size(); + const auto sector_size = sector_table_.sector_size(); + + for (auto sector : sectors) + { + auto position = sector * short_sector_size; + auto master_allocation_table_index = position / sector_size; + + auto sector_data = load_sectors({ short_container_stream_[master_allocation_table_index] }); + + auto offset = position % sector_size; + result.append(sectors_.data(), offset, short_sector_size); + } + + return result; + } + + std::vector load_msat(byte_vector &/*data*/) + { + const auto sector_size = header_.sector_size(); + auto master_sectors = header_.sectors(); + + if (header_.num_master_sectors() > 109) + { + auto current_sector = header_.master_table_start(); + + for (auto r = std::size_t(0); r < header_.num_master_sectors(); ++r) + { + auto msat = load_sectors({ current_sector }); + auto index = 0; + + while (index < (sector_size - 1) / sizeof(sector_id)) + { + master_sectors.push_back(msat.read()); + } + + current_sector = msat.read(); + } + } + + return master_sectors; + } + + void load(byte_vector &data) + { + header_.load(data); + + const auto sector_size = header_.sector_size(); + const auto short_sector_size = header_.short_sector_size(); + + sector_table_.sector_size(sector_size); + short_sector_table_.sector_size(short_sector_size); + + sector_table_.load(load_sectors(load_msat(data))); + short_sector_table_.load(load_sectors(sector_table_.follow(header_.short_table_start()))); + auto directory_data = load_sectors(sector_table_.follow(header_.directory_start())); + directory_.load(directory_data); + + auto first_short_sector = directory_.entry(u"Root Entry", false).first; + short_container_stream_ = sector_table_.follow(first_short_sector); + } + + byte_vector save() const + { + auto result = byte_vector(); + + result.append(header_.save().data()); + result.append(sector_table_.save().data()); + result.append(short_sector_table_.save().data()); + result.append(directory_.save().data()); + + return result; + } + + bool has_stream(const std::u16string &filename) const + { + //TODO: do this the right way + try + { + directory_.entry(filename); + } + catch (xlnt::exception) + { + return false; + } + + return true; + } + + void add_stream( + const std::u16string &name, + const byte_vector &/*data*/) + { + auto entry = directory_.entry(name, !has_stream(name)); + } + + byte_vector stream(const std::u16string &name) const + { + if (!has_stream(name)) + { + throw xlnt::exception("document doesn't contain stream with the given name"); + } + + auto entry = directory_.entry(name); + byte_vector result; + + if (entry.size < header_.threshold()) + { + result = load_sectors(short_sector_table_.follow(entry.first)); + result.resize(entry.size); + } + else + { + result = load_sectors(sector_table_.follow(entry.first)); + result.resize(entry.size); + } + + return result; + } + +private: directory_tree directory_; header header_; - allocation_table small_block_table_; - allocation_table big_block_table_; - std::vector small_blocks_; + allocation_table sector_table_; + byte_vector sectors_; + allocation_table short_sector_table_; + byte_vector short_sectors_; + std::vector short_container_stream_; }; -std::vector load_big_blocks(const std::vector &blocks, compound_document_impl &d) -{ - std::vector result; - auto bytes_loaded = std::size_t(0); - const auto block_size = d.big_block_table_.blockSize; - - for (auto block : blocks) - { - auto position = block_size * (block + 1); - auto block_length = std::size_t(512); - auto current_size = result.size(); - result.resize(result.size() + block_length); - - std::copy( - d.buffer_.begin() + position, - d.buffer_.begin() + position + block_length, - result.begin() + current_size); - - bytes_loaded += block_length; - } - - return result; -} - -std::vector load_small_blocks(const std::vector &blocks, compound_document_impl &d) -{ - std::vector result; - auto bytes_loaded = std::size_t(0); - const auto small_block_size = d.small_block_table_.blockSize; - const auto big_block_size = d.big_block_table_.blockSize; - - for (auto block : blocks) - { - auto position = block * small_block_size; - auto bbindex = position / big_block_size; - - if (bbindex >= d.small_blocks_.size()) break; - - auto block_data = load_big_blocks({ d.small_blocks_[bbindex] }, d); - - auto offset = position % big_block_size; - auto current_size = result.size(); - result.resize(result.size() + small_block_size); - - std::copy( - block_data.begin() + offset, - block_data.begin() + offset + small_block_size, - result.begin() + current_size); - - bytes_loaded += small_block_size; - } - - return result; -} - compound_document::compound_document() : d_(new compound_document_impl()) { - d_->header_ = initialize_header(); - - d_->big_block_table_.blockSize = static_cast(1) << d_->header_.big_shift; - d_->small_block_table_.blockSize = static_cast(1) << d_->header_.small_shift; } compound_document::~compound_document() { } -void compound_document::load(const std::vector &data) +compound_document_impl &compound_document::impl() { - d_->buffer_ = data; + return *d_; +} - // 1. load header - { - if (data.size() < 512) - { - throw xlnt::exception("not ole"); - } +compound_document_impl &compound_document::impl() const +{ + return *d_; +} - d_->header_ = load_header(data); - if (d_->header_.id != pole_magic) - { - throw xlnt::exception("not ole"); - } - - if (!header_is_valid(d_->header_) || d_->header_.threshold != 4096) - { - throw xlnt::exception("bad ole"); - } - } - - // important block size - auto big_block_size = static_cast(1) << d_->header_.big_shift; - d_->big_block_table_.blockSize = big_block_size; - auto small_block_size = static_cast(1) << d_->header_.small_shift; - d_->small_block_table_.blockSize = small_block_size; - - // find blocks allocated to store big bat - // the first 109 blocks are in header, the rest in meta bat - auto num_header_blocks = std::min(std::uint32_t(109), d_->header_.num_big_blocks); - auto blocks = std::vector( - d_->header_.bb_blocks.begin(), - d_->header_.bb_blocks.begin() + num_header_blocks); - auto buffer = byte_vector(); - - if ((d_->header_.num_big_blocks > 109) && (d_->header_.num_meta_blocks > 0)) - { - buffer.resize(big_block_size); - - std::size_t k = 109; - - for (std::size_t r = 0; r < d_->header_.num_meta_blocks; r++) - { - load_big_blocks(blocks, *d_); - - for (std::size_t s = 0; s < big_block_size - 4; s += 4) - { - if (k >= d_->header_.num_big_blocks) break; - auto offset = s; - blocks[k++] = xlnt::detail::read_int(buffer, offset); - } - - auto offset = big_block_size - 4; - xlnt::detail::read_int(buffer, offset); - } - } - - // load big block table - if (!blocks.empty()) - { - d_->big_block_table_.load(load_big_blocks(blocks, *d_)); - } - - // load small bat - blocks = d_->big_block_table_.follow(d_->header_.small_start); - - if (!blocks.empty()) - { - d_->small_block_table_.load(load_big_blocks(blocks, *d_)); - } - - // load directory tree - blocks = d_->big_block_table_.follow(d_->header_.directory_start); - auto directory_data = load_big_blocks(blocks, *d_); - d_->directory_.load(directory_data); - - auto offset = std::size_t(0x74); - auto sb_start = xlnt::detail::read_int(directory_data, offset); - - // fetch block chain as data for small-files - d_->small_blocks_ = d_->big_block_table_.follow(sb_start); // small files +void compound_document::load(std::vector &data) +{ + byte_vector vec(data); + return impl().load(vec); } std::vector compound_document::save() const { - return d_->buffer_; + return impl().save().data(); } -bool compound_document::has_stream(const std::string &filename) const +bool compound_document::has_stream(const std::u16string &filename) const { - return d_->directory_.entry(filename, false) != nullptr; + return impl().has_stream(filename); } -void compound_document::add_stream( - const std::string &name, - const std::vector &/*data*/) +void compound_document::add_stream(const std::u16string &name, const std::vector &data) { - d_->directory_.entry(name, !has_stream(name)); + return impl().add_stream(name, data); } -std::vector compound_document::stream(const std::string &name) const +std::vector compound_document::stream(const std::u16string &name) const { - if (!has_stream(name)) - { - throw xlnt::exception("document doesn't contain stream with the given name"); - } - - auto entry = d_->directory_.entry(name); - byte_vector result; - - if (entry->size < d_->header_.threshold) - { - result = load_small_blocks(d_->small_block_table_.follow(entry->start), *d_); - result.resize(entry->size); - } - else - { - result = load_big_blocks(d_->big_block_table_.follow(entry->start), *d_); - result.resize(entry->size); - } - - return result; + return impl().stream(name).data(); } } // namespace detail diff --git a/source/detail/cryptography/compound_document.hpp b/source/detail/cryptography/compound_document.hpp index ddcf574b..a0c07d03 100644 --- a/source/detail/cryptography/compound_document.hpp +++ b/source/detail/cryptography/compound_document.hpp @@ -33,7 +33,7 @@ namespace xlnt { namespace detail { -struct compound_document_impl; +class compound_document_impl; class compound_document { @@ -41,14 +41,17 @@ public: compound_document(); ~compound_document(); - void load(const std::vector &data); + void load(std::vector &data); std::vector save() const; - bool has_stream(const std::string &filename) const; - void add_stream(const std::string &filename, const std::vector &data); - std::vector stream(const std::string &filename) const; + bool has_stream(const std::u16string &filename) const; + void add_stream(const std::u16string &filename, const std::vector &data); + std::vector stream(const std::u16string &filename) const; private: + compound_document_impl &impl(); + //TODO: can this return a const reference? + compound_document_impl &impl() const; std::unique_ptr d_; }; diff --git a/source/detail/cryptography/encryption_info.cpp b/source/detail/cryptography/encryption_info.cpp index 8695a4e9..95a4a168 100644 --- a/source/detail/cryptography/encryption_info.cpp +++ b/source/detail/cryptography/encryption_info.cpp @@ -37,7 +37,7 @@ std::vector calculate_standard_key( { // H_0 = H(salt + password) auto salt_plus_password = info.salt; - auto password_bytes = xlnt::detail::to_bytes(password.begin(), password.end()); + auto password_bytes = xlnt::detail::byte_vector::from(password).data(); std::copy(password_bytes.begin(), password_bytes.end(), std::back_inserter(salt_plus_password)); @@ -107,7 +107,7 @@ std::vector calculate_agile_key( { // H_0 = H(salt + password) auto salt_plus_password = info.key_encryptor.salt_value; - auto password_bytes = xlnt::detail::to_bytes(password.begin(), password.end()); + auto password_bytes = xlnt::detail::byte_vector::from(password).data(); std::copy(password_bytes.begin(), password_bytes.end(), std::back_inserter(salt_plus_password)); diff --git a/source/detail/cryptography/xlsx_crypto_consumer.cpp b/source/detail/cryptography/xlsx_crypto_consumer.cpp index d0f8ba96..d98c8c6f 100644 --- a/source/detail/cryptography/xlsx_crypto_consumer.cpp +++ b/source/detail/cryptography/xlsx_crypto_consumer.cpp @@ -46,13 +46,13 @@ using xlnt::detail::encryption_info; std::vector decrypt_xlsx_standard( encryption_info info, - const byte_vector &encrypted_package) + const std::vector &encrypted_package) { const auto key = info.calculate_key(); - auto offset = std::size_t(0); - auto decrypted_size = xlnt::detail::read_int(encrypted_package, offset); - auto decrypted = xlnt::detail::aes_ecb_decrypt(encrypted_package, key, offset); + auto reader = xlnt::detail::byte_reader(encrypted_package); + auto decrypted_size = reader.read(); + auto decrypted = xlnt::detail::aes_ecb_decrypt(encrypted_package, key, reader.offset()); decrypted.resize(static_cast(decrypted_size)); return decrypted; @@ -108,14 +108,17 @@ encryption_info read_standard_encryption_info(const std::vector &i result.is_agile = false; auto &standard_info = result.standard; - using xlnt::detail::read_int; - auto offset = std::size_t(8); // skip version info + auto reader = xlnt::detail::byte_reader(info_bytes); - auto header_length = read_int(info_bytes, offset); - auto index_at_start = offset; - /*auto skip_flags = */ read_int(info_bytes, offset); - /*auto size_extra = */ read_int(info_bytes, offset); - auto alg_id = read_int(info_bytes, offset); + // skip version info + reader.read(); + reader.read(); + + auto header_length = reader.read(); + auto index_at_start = reader.offset(); + /*auto skip_flags = */ reader.read(); + /*auto size_extra = */ reader.read(); + auto alg_id = reader.read(); if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610) { @@ -126,60 +129,59 @@ encryption_info read_standard_encryption_info(const std::vector &i throw xlnt::exception("invalid cipher algorithm"); } - auto alg_id_hash = read_int(info_bytes, offset); + auto alg_id_hash = reader.read(); if (alg_id_hash != 0x00008004 && alg_id_hash == 0) { throw xlnt::exception("invalid hash algorithm"); } - standard_info.key_bits = read_int(info_bytes, offset); + standard_info.key_bits = reader.read(); standard_info.key_bytes = standard_info.key_bits / 8; - auto provider_type = read_int(info_bytes, offset); + auto provider_type = reader.read(); if (provider_type != 0 && provider_type != 0x00000018) { throw xlnt::exception("invalid provider type"); } - read_int(info_bytes, offset); // reserved 1 - if (read_int(info_bytes, offset) != 0) // reserved 2 + reader.read(); // reserved 1 + if (reader.read() != 0) // reserved 2 { throw xlnt::exception("invalid header"); } - const auto csp_name_length = header_length - (offset - index_at_start); + const auto csp_name_length = header_length - (reader.offset() - index_at_start); std::vector csp_name_wide( - reinterpret_cast(&*(info_bytes.begin() + static_cast(offset))), + reinterpret_cast(&*(info_bytes.begin() + static_cast(reader.offset()))), reinterpret_cast( - &*(info_bytes.begin() + static_cast(offset + csp_name_length)))); + &*(info_bytes.begin() + static_cast(reader.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(info_bytes, offset); + const auto salt_size = reader.read(); standard_info.salt = std::vector( - info_bytes.begin() + static_cast(offset), - info_bytes.begin() + static_cast(offset + salt_size)); - offset += salt_size; + info_bytes.begin() + static_cast(reader.offset()), + info_bytes.begin() + static_cast(reader.offset() + salt_size)); + reader.offset(reader.offset() + salt_size); static const auto verifier_size = std::size_t(16); standard_info.encrypted_verifier = std::vector( - info_bytes.begin() + static_cast(offset), - info_bytes.begin() + static_cast(offset + verifier_size)); - offset += verifier_size; + info_bytes.begin() + static_cast(reader.offset()), + info_bytes.begin() + static_cast(reader.offset() + verifier_size)); + reader.offset(reader.offset() + verifier_size); - const auto verifier_hash_size = read_int(info_bytes, offset); + const auto verifier_hash_size = reader.read(); const auto encrypted_verifier_hash_size = std::size_t(32); standard_info.encrypted_verifier_hash = std::vector( - info_bytes.begin() + static_cast(offset), - info_bytes.begin() + static_cast(offset + encrypted_verifier_hash_size)); - offset += encrypted_verifier_hash_size; + info_bytes.begin() + static_cast(reader.offset()), + info_bytes.begin() + static_cast(reader.offset() + encrypted_verifier_hash_size)); + reader.offset(reader.offset() + encrypted_verifier_hash_size); - if (offset != info_bytes.size()) + if (reader.offset() != info_bytes.size()) { throw xlnt::exception("extra data after encryption info"); } @@ -274,12 +276,11 @@ encryption_info read_encryption_info(const std::vector &info_bytes { encryption_info info; - using xlnt::detail::read_int; - std::size_t offset = 0; + auto reader = xlnt::detail::byte_reader(info_bytes); - auto version_major = read_int(info_bytes, offset); - auto version_minor = read_int(info_bytes, offset); - auto encryption_flags = read_int(info_bytes, offset); + auto version_major = reader.read(); + auto version_minor = reader.read(); + auto encryption_flags = reader.read(); // version 4.4 is agile if (version_major == 4 && version_minor == 4) @@ -327,11 +328,11 @@ std::vector decrypt_xlsx( } xlnt::detail::compound_document document; - document.load(bytes); + document.load(const_cast &>(bytes)); - auto encryption_info = read_encryption_info(document.stream("EncryptionInfo")); + auto encryption_info = read_encryption_info(document.stream(u"EncryptionInfo")); encryption_info.password = password; - auto encrypted_package = document.stream("EncryptedPackage"); + auto encrypted_package = document.stream(u"EncryptedPackage"); return encryption_info.is_agile ? decrypt_xlsx_agile(encryption_info, encrypted_package) diff --git a/source/detail/cryptography/xlsx_crypto_producer.cpp b/source/detail/cryptography/xlsx_crypto_producer.cpp index e6f8ba28..f4166fd9 100644 --- a/source/detail/cryptography/xlsx_crypto_producer.cpp +++ b/source/detail/cryptography/xlsx_crypto_producer.cpp @@ -171,10 +171,10 @@ std::vector encrypt_xlsx( xlnt::detail::compound_document document; - document.add_stream("EncryptionInfo", encryption_info.is_agile + document.add_stream(u"EncryptionInfo", encryption_info.is_agile ? write_agile_encryption_info(encryption_info) : write_standard_encryption_info(encryption_info)); - document.add_stream("EncryptedPackage", encryption_info.is_agile + document.add_stream(u"EncryptedPackage", encryption_info.is_agile ? encrypt_xlsx_agile(encryption_info, plaintext) : encrypt_xlsx_standard(encryption_info, plaintext));