This commit is contained in:
Thomas Fussell 2017-04-28 17:56:05 -04:00
parent be37df3c86
commit f5b5d67594
7 changed files with 396 additions and 288 deletions

View File

@ -293,5 +293,32 @@ std::vector<byte> string_to_bytes(const std::basic_string<T> &string)
return bytes;
}
template<typename T>
T read(std::istream &in)
{
T result;
in.read(reinterpret_cast<char *>(&result), sizeof(T));
return result;
}
template<typename T>
std::vector<T> read_vector(std::istream &in, std::size_t count)
{
std::vector<T> result(count, T());
in.read(reinterpret_cast<char *>(&result[0]), sizeof(T) * count);
return result;
}
template<typename T>
std::basic_string<T> read_string(std::istream &in, std::size_t count)
{
std::basic_string<T> result(count, T());
in.read(reinterpret_cast<char *>(&result[0]), sizeof(T) * count);
return result;
}
} // namespace detail
} // namespace xlnt

View File

@ -76,77 +76,246 @@ const directory_id End = -1;
namespace xlnt {
namespace detail {
compound_document::compound_document(std::vector<std::uint8_t> &data)
: writer_(new binary_writer<byte>(data)),
reader_(new binary_reader<byte>(data))
/// <summary>
/// Allows a std::vector to be read through a std::istream.
/// </summary>
class compound_document_istreambuf : public std::streambuf
{
header_.msat.fill(FreeSector);
writer_->offset(0);
writer_->write(header_);
using int_type = std::streambuf::int_type;
header_.directory_start = allocate_sector();
writer_->offset(0);
writer_->write(header_);
insert_entry("Root Entry", compound_document_entry::entry_type::RootStorage);
public:
compound_document_istreambuf(const std::string &filename)
: data_(filename.begin(), filename.end()),
position_(0)
{
}
compound_document::compound_document(const std::vector<std::uint8_t> &data)
: writer_(nullptr),
reader_(new binary_reader<byte>(data))
{
header_ = reader_->read<compound_document_header>();
compound_document_istreambuf(const compound_document_istreambuf &) = delete;
compound_document_istreambuf &operator=(const compound_document_istreambuf &) = delete;
// read msat
auto current = header_.extra_msat_start;
private:
int_type underflow()
{
if (position_ == data_.size())
{
return traits_type::eof();
}
for (auto i = std::size_t(0); i < header_.num_msat_sectors; ++i)
return traits_type::to_int_type(static_cast<char>(data_[position_]));
}
int_type uflow()
{
if (i < 109)
if (position_ == data_.size())
{
msat_.push_back(header_.msat[i]);
return traits_type::eof();
}
return traits_type::to_int_type(static_cast<char>(data_[position_++]));
}
std::streamsize showmanyc()
{
if (position_ == data_.size())
{
return static_cast<std::streamsize>(-1);
}
return static_cast<std::streamsize>(data_.size() - position_);
}
std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode)
{
if (way == std::ios_base::beg)
{
position_ = 0;
}
else if (way == std::ios_base::end)
{
position_ = data_.size();
}
if (off < 0)
{
if (static_cast<std::size_t>(-off) > position_)
{
position_ = 0;
return static_cast<std::ptrdiff_t>(-1);
}
else
{
auto extra_msat_sector = std::vector<sector_id>();
auto extra_msat_sector_writer = binary_writer<sector_id>(extra_msat_sector);
read_sector(current, extra_msat_sector_writer);
std::copy(extra_msat_sector.begin(),
extra_msat_sector.end() - 1,
std::back_inserter(msat_));
current = extra_msat_sector.back();
position_ -= static_cast<std::size_t>(-off);
}
}
for (auto sat_sector_id : msat_)
else if (off > 0)
{
if (sat_sector_id < 0) continue;
auto sat_sector = std::vector<sector_id>();
auto sat_sector_writer = binary_writer<sector_id>(sat_sector);
read_sector(sat_sector_id, sat_sector_writer);
std::copy(sat_sector.begin(),
sat_sector.end(),
std::back_inserter(sat_));
}
for (auto ssat_sector_id : follow_chain(header_.ssat_start, sat_))
if (static_cast<std::size_t>(off) + position_ > data_.size())
{
auto ssat_sector = std::vector<sector_id>();
auto ssat_sector_writer = binary_writer<sector_id>(ssat_sector);
read_sector(ssat_sector_id, ssat_sector_writer);
std::copy(ssat_sector.begin(),
ssat_sector.end(),
std::back_inserter(ssat_));
position_ = data_.size();
return static_cast<std::ptrdiff_t>(-1);
}
else
{
position_ += static_cast<std::size_t>(off);
}
}
tree_initialize_parent_maps();
return static_cast<std::ptrdiff_t>(position_);
}
std::streampos seekpos(std::streampos sp, std::ios_base::openmode)
{
if (sp < 0)
{
position_ = 0;
}
else if (static_cast<std::size_t>(sp) > data_.size())
{
position_ = data_.size();
}
else
{
position_ = static_cast<std::size_t>(sp);
}
return static_cast<std::ptrdiff_t>(position_);
}
private:
std::vector<std::uint8_t> data_;
std::size_t position_;
};
/// <summary>
/// Allows a std::vector to be written through a std::ostream.
/// </summary>
class compound_document_ostreambuf : public std::streambuf
{
using int_type = std::streambuf::int_type;
public:
compound_document_ostreambuf(const std::string &filename)
: data_(filename.begin(), filename.end()),
position_(0)
{
}
compound_document_ostreambuf(const compound_document_ostreambuf &) = delete;
compound_document_ostreambuf &operator=(const compound_document_ostreambuf &) = delete;
private:
int_type overflow(int_type c = traits_type::eof())
{
if (c != traits_type::eof())
{
data_.push_back(static_cast<std::uint8_t>(c));
position_ = data_.size() - 1;
}
return traits_type::to_int_type(static_cast<char>(data_[position_]));
}
std::streamsize xsputn(const char *s, std::streamsize n)
{
if (data_.empty())
{
data_.resize(static_cast<std::size_t>(n));
}
else
{
auto position_size = data_.size();
auto required_size = static_cast<std::size_t>(position_ + static_cast<std::size_t>(n));
data_.resize(std::max(position_size, required_size));
}
std::copy(s, s + n, data_.begin() + static_cast<std::ptrdiff_t>(position_));
position_ += static_cast<std::size_t>(n);
return n;
}
std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode)
{
if (way == std::ios_base::beg)
{
position_ = 0;
}
else if (way == std::ios_base::end)
{
position_ = data_.size();
}
if (off < 0)
{
if (static_cast<std::size_t>(-off) > position_)
{
position_ = 0;
return static_cast<std::ptrdiff_t>(-1);
}
else
{
position_ -= static_cast<std::size_t>(-off);
}
}
else if (off > 0)
{
if (static_cast<std::size_t>(off) + position_ > data_.size())
{
position_ = data_.size();
return static_cast<std::ptrdiff_t>(-1);
}
else
{
position_ += static_cast<std::size_t>(off);
}
}
return static_cast<std::ptrdiff_t>(position_);
}
std::streampos seekpos(std::streampos sp, std::ios_base::openmode)
{
if (sp < 0)
{
position_ = 0;
}
else if (static_cast<std::size_t>(sp) > data_.size())
{
position_ = data_.size();
}
else
{
position_ = static_cast<std::size_t>(sp);
}
return static_cast<std::ptrdiff_t>(position_);
}
private:
std::vector<std::uint8_t> data_;
std::size_t position_;
};
compound_document::compound_document(std::ostream &out)
: out_(&out),
stream_in_(nullptr),
stream_out_(nullptr)
{
write_header();
insert_entry("Root Entry", compound_document_entry::entry_type::RootStorage);
}
compound_document::compound_document(std::istream &in)
: in_(&in),
stream_in_(nullptr),
stream_out_(nullptr)
{
read_header();
read_msat();
read_sat();
read_ssat();
read_directory();
}
compound_document::~compound_document()
@ -163,84 +332,36 @@ std::size_t compound_document::short_sector_size()
return static_cast<std::size_t>(1) << header_.short_sector_size_power;
}
std::vector<byte> compound_document::read_stream(const std::string &name)
std::istream &compound_document::open_read_stream(const std::string &name)
{
const auto entry_id = find_entry(name, compound_document_entry::entry_type::UserStream);
const auto &entry = entries_.at(entry_id);
auto stream_data = std::vector<byte>();
auto stream_data_writer = binary_writer<byte>(stream_data);
stream_in_buffer_.reset(new compound_document_istreambuf(name));
stream_in_.rdbuf(stream_in_buffer_.get());
if (entry.size < header_.threshold)
{
for (auto sector : follow_chain(entry.start, ssat_))
{
read_short_sector<byte>(sector, stream_data_writer);
}
}
else
{
for (auto sector : follow_chain(entry.start, sat_))
{
read_sector<byte>(sector, stream_data_writer);
}
}
stream_data.resize(entry.size);
return stream_data;
return stream_in_;
}
void compound_document::write_stream(const std::string &name, const std::vector<std::uint8_t> &data)
std::ostream &compound_document::open_write_stream(const std::string &name)
{
auto entry_id = contains_entry(name, compound_document_entry::entry_type::UserStream)
? find_entry(name, compound_document_entry::entry_type::UserStream)
: insert_entry(name, compound_document_entry::entry_type::UserStream);
auto &entry = entries_.at(entry_id);
entry.size = static_cast<std::uint32_t>(data.size());
auto stream_data_reader = binary_reader<byte>(data);
stream_out_buffer_.reset(new compound_document_ostreambuf(name));
stream_out_.rdbuf(stream_out_buffer_.get());
if (entry.size < header_.threshold)
{
const auto num_sectors = data.size() / short_sector_size()
+ (data.size() % short_sector_size() ? 1 : 0);
auto chain = allocate_short_sectors(num_sectors);
entry.start = chain.front();
for (auto sector : follow_chain(entry.start, ssat_))
{
write_short_sector(stream_data_reader, sector);
}
}
else
{
const auto num_sectors = data.size() / short_sector_size()
+ (data.size() % short_sector_size() ? 1 : 0);
auto chain = allocate_sectors(num_sectors);
entry.start = chain.front();
for (auto sector : follow_chain(entry.start, sat_))
{
write_sector(stream_data_reader, sector);
}
}
auto directory_chain = follow_chain(header_.directory_start, sat_);
const auto entries_per_sector = sector_size() / sizeof(compound_document_entry);
auto entry_directory_sector = directory_chain[entry_id / entries_per_sector];
writer_->offset(sector_data_start()
+ entry_directory_sector * sector_size()
+ (entry_id % entries_per_sector) * sizeof(compound_document_entry));
writer_->write(entry);
return stream_out_;
}
template<typename T>
void compound_document::write_sector(binary_reader<T> &reader, sector_id id)
{
writer_->offset(sector_data_start() + sector_size() * id);
writer_->append(reader, std::min(sector_size(), reader.bytes()) / sizeof(T));
out_->seekp(sector_data_start() + sector_size() * id);
out_->write(reinterpret_cast<const char *>(reader.data() + reader.offset()),
std::min(sector_size(), reader.bytes() - reader.offset()));
}
template<typename T>
@ -249,15 +370,18 @@ void compound_document::write_short_sector(binary_reader<T> &reader, sector_id i
auto chain = follow_chain(entries_[0].start, sat_);
auto sector_id = chain[id / (sector_size() / short_sector_size())];
auto sector_offset = id % (sector_size() / short_sector_size()) * short_sector_size();
writer_->offset(sector_data_start() + sector_size() * sector_id + sector_offset);
writer_->append(reader, std::min(short_sector_size() / sizeof(T), reader.count() - reader.offset()));
out_->seekp(sector_data_start() + sector_size() * sector_id + sector_offset);
out_->write(reinterpret_cast<const char *>(reader.data() + reader.offset()),
std::min(short_sector_size(), reader.bytes() - reader.offset()));
}
template<typename T>
void compound_document::read_sector(sector_id id, binary_writer<T> &writer)
{
reader_->offset(sector_data_start() + sector_size() * id);
writer.append(*reader_, sector_size());
in_->seekg(sector_data_start() + sector_size() * id);
std::vector<byte> sector(sector_size(), 0);
in_->read(reinterpret_cast<char *>(sector.data()), sector_size());
writer.append(sector);
}
template<typename T>
@ -300,12 +424,7 @@ sector_id compound_document::allocate_sector()
auto next_free = sector_id(next_free_iter - sat_.begin());
sat_[next_free] = EndOfChain;
auto next_free_msat_index = next_free / sectors_per_sector;;
auto sat_index = msat_[next_free_msat_index];
writer_->offset(sector_data_start()
+ (sat_index * sector_size())
+ (next_free % sectors_per_sector) * sizeof(sector_id));
writer_->write(EndOfChain);
write_sat();
auto empty_sector = std::vector<byte>(sector_size());
auto empty_sector_reader = binary_reader<byte>(empty_sector);
@ -387,8 +506,7 @@ sector_id compound_document::allocate_short_sector()
sat_[ssat_chain.back()] = new_ssat_sector_id;
}
writer_->offset(0);
writer_->write(header_);
write_header();
auto old_size = ssat_.size();
ssat_.resize(old_size + sectors_per_sector, FreeSector);
@ -403,13 +521,7 @@ sector_id compound_document::allocate_short_sector()
auto next_free = sector_id(next_free_iter - ssat_.begin());
ssat_[next_free] = EndOfChain;
auto sat_chain = follow_chain(header_.ssat_start, sat_);
auto next_free_sat_chain_index = next_free / sectors_per_sector;
auto sat_index = sat_chain[next_free_sat_chain_index];
writer_->offset(sector_data_start()
+ (sat_index * sectors_per_sector) * sizeof(sector_id)
+ (next_free % sectors_per_sector) * sizeof(sector_id));
writer_->write(EndOfChain);
write_ssat();
const auto short_sectors_per_sector = sector_size() / short_sector_size();
const auto required_container_sectors = std::size_t(next_free / short_sectors_per_sector + 1);
@ -419,8 +531,7 @@ sector_id compound_document::allocate_short_sector()
if (entries_[0].start < 0)
{
entries_[0].start = allocate_sector();
writer_->offset(sector_data_start() + header_.directory_start * sector_size());
writer_->write(entries_[0]);
write_entry(0);
}
auto container_chain = follow_chain(entries_[0].start, sat_);
@ -428,6 +539,7 @@ sector_id compound_document::allocate_short_sector()
if (required_container_sectors > container_chain.size())
{
sat_[container_chain.back()] = allocate_sector();
write_sat();
}
}
@ -454,17 +566,13 @@ directory_id compound_document::next_empty_entry()
/ sizeof(compound_document_entry);
auto new_sector = allocate_sector();
// TODO: connect chains here
writer_->offset(sector_data_start() + new_sector * sector_size());
reader_->offset(sector_data_start() + new_sector * sector_size());
for (auto i = std::size_t(0); i < entries_per_sector; ++i)
{
auto empty_entry = compound_document_entry();
empty_entry.type = compound_document_entry::entry_type::Empty;
writer_->write(empty_entry);
entries_.push_back(reader_->read<compound_document_entry>());
entries_.push_back(empty_entry);
write_entry(entry_id + directory_id(i));
}
return entry_id;
@ -480,14 +588,7 @@ directory_id compound_document::insert_entry(
entry.name(name);
entry.type = type;
//TODO: move this to a "write_entry" function
auto directory_chain = follow_chain(header_.directory_start, sat_);
const auto entries_per_sector = sector_size() / sizeof(compound_document_entry);
auto entry_directory_sector = directory_chain[entry_id / entries_per_sector];
writer_->offset(sector_data_start()
+ entry_directory_sector * sector_size()
+ (entry_id % entries_per_sector) * sizeof(compound_document_entry));
writer_->write(entry);
write_entry(entry_id);
// TODO: parse path from name and use correct parent storage instead of 0
tree_insert(entry_id, 0);
@ -542,18 +643,17 @@ void compound_document::print_directory()
}
}
void compound_document::tree_initialize_parent_maps()
void compound_document::read_directory()
{
const auto entries_per_sector = sector_size() / sizeof(compound_document_entry);
auto entry_id = directory_id(0);
for (auto sector : follow_chain(header_.directory_start, sat_))
{
reader_->offset(sector_data_start() + sector * sector_size());
for (auto i = std::size_t(0); i < entries_per_sector; ++i)
{
entries_.push_back(reader_->read<compound_document_entry>());
entries_.push_back(compound_document_entry());
read_entry(entry_id++);
}
}
@ -843,8 +943,8 @@ compound_document_entry::entry_color &compound_document::tree_color(directory_id
void compound_document::read_header()
{
reader_->offset(0);
header_ = reader_->read<compound_document_header>();
in_->seekg(0, std::ios::beg);
in_->read(reinterpret_cast<char *>(&header_), sizeof(compound_document_header));
}
void compound_document::read_msat()
@ -905,14 +1005,14 @@ void compound_document::read_entry(directory_id id)
const auto offset = sector_size() * directory_sector
+ ((id % entries_per_sector) * sizeof(compound_document_entry));
reader_->offset(offset);
entries_[id] = reader_->read<compound_document_entry>();
in_->seekg(sector_data_start() + offset, std::ios::beg);
in_->read(reinterpret_cast<char *>(&entries_[id]), sizeof(compound_document_entry));
}
void compound_document::write_header()
{
writer_->offset(0);
writer_->write<compound_document_header>(header_);
out_->seekp(0, std::ios::beg);
out_->write(reinterpret_cast<char *>(&header_), sizeof(compound_document_header));
}
void compound_document::write_msat()
@ -968,8 +1068,8 @@ void compound_document::write_entry(directory_id id)
const auto offset = sector_size() * directory_sector
+ ((id % entries_per_sector) * sizeof(compound_document_entry));
writer_->offset(offset);
writer_->write<compound_document_entry>(entries_[id]);
out_->seekp(offset, std::ios::beg);
out_->write(reinterpret_cast<char *>(&entries_[id]), sizeof(compound_document_entry));
}
} // namespace detail

View File

@ -113,15 +113,20 @@ struct compound_document_entry
std::uint32_t ignore2;
};
class compound_document_istreambuf;
class compound_document_ostreambuf;
class compound_document
{
public:
compound_document(std::vector<std::uint8_t> &data);
compound_document(const std::vector<std::uint8_t> &data);
compound_document(std::istream &in);
compound_document(std::ostream &out);
~compound_document();
std::vector<std::uint8_t> read_stream(const std::string &filename);
void write_stream(const std::string &filename, const std::vector<std::uint8_t> &data);
void close();
std::istream &open_read_stream(const std::string &filename);
std::ostream &open_write_stream(const std::string &filename);
private:
template<typename T>
@ -139,6 +144,7 @@ private:
void read_sat();
void read_ssat();
void read_entry(directory_id id);
void read_directory();
void write_header();
void write_msat();
@ -172,7 +178,6 @@ private:
compound_document_entry::entry_type type);
// Red black tree helper functions
void tree_initialize_parent_maps();
void tree_insert(directory_id new_id, directory_id storage_id);
void tree_insert_fixup(directory_id x);
std::string tree_path(directory_id id);
@ -186,9 +191,6 @@ private:
std::string tree_key(directory_id id);
compound_document_entry::entry_color &tree_color(directory_id id);
std::unique_ptr<binary_writer<byte>> writer_;
std::unique_ptr<binary_reader<byte>> reader_;
compound_document_header header_;
sector_chain msat_;
sector_chain sat_;
@ -197,6 +199,14 @@ private:
std::unordered_map<directory_id, directory_id> parent_storage_;
std::unordered_map<directory_id, directory_id> parent_;
std::istream *in_;
std::ostream *out_;
std::unique_ptr<compound_document_istreambuf> stream_in_buffer_;
std::istream stream_in_;
std::unique_ptr<compound_document_ostreambuf> stream_out_buffer_;
std::ostream stream_out_;
};
} // namespace detail

View File

@ -42,18 +42,25 @@
namespace {
using xlnt::detail::byte;
using xlnt::detail::binary_reader;
using xlnt::detail::read;
using xlnt::detail::encryption_info;
std::vector<std::uint8_t> decrypt_xlsx_standard(
encryption_info info,
const std::vector<std::uint8_t> &encrypted_package)
std::istream &encrypted_package_stream)
{
const auto key = info.calculate_key();
auto reader = binary_reader<byte>(encrypted_package);
auto decrypted_size = reader.read<std::uint64_t>();
auto decrypted = xlnt::detail::aes_ecb_decrypt(encrypted_package, key, reader.offset());
auto encrypted_package = std::vector<byte>(
std::istreambuf_iterator<char>(encrypted_package_stream),
std::istreambuf_iterator<char>());
auto decrypted_size = read<std::uint64_t>(encrypted_package_stream);
auto decrypted = xlnt::detail::aes_ecb_decrypt(
encrypted_package,
key,
encrypted_package_stream.tellg());
decrypted.resize(static_cast<std::size_t>(decrypted_size));
return decrypted;
@ -61,10 +68,8 @@ std::vector<std::uint8_t> decrypt_xlsx_standard(
std::vector<std::uint8_t> decrypt_xlsx_agile(
const encryption_info &info,
const std::vector<std::uint8_t> &encrypted_package)
std::istream &encrypted_package_stream)
{
static const auto segment_length = std::size_t(4096);
const auto key = info.calculate_key();
auto salt_size = info.agile.key_data.salt_size;
@ -72,23 +77,20 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(
salt_with_block_key.resize(salt_size + sizeof(std::uint32_t), 0);
auto &segment = *reinterpret_cast<std::uint32_t *>(salt_with_block_key.data() + salt_size);
auto total_size = static_cast<std::size_t>(*reinterpret_cast<const std::uint64_t *>(encrypted_package.data()));
auto total_size = read<std::uint64_t>(encrypted_package_stream);
std::vector<std::uint8_t> encrypted_segment(segment_length, 0);
std::vector<std::uint8_t> encrypted_segment(4096, 0);
std::vector<std::uint8_t> decrypted_package;
decrypted_package.reserve(encrypted_package.size() - 8);
for (std::size_t i = 8; i < encrypted_package.size(); i += segment_length)
while (encrypted_package_stream)
{
auto iv = hash(info.agile.key_encryptor.hash, salt_with_block_key);
iv.resize(16);
auto segment_begin = encrypted_package.begin() + static_cast<std::ptrdiff_t>(i);
auto current_segment_length = std::min(segment_length, encrypted_package.size() - i);
auto segment_end = encrypted_package.begin() + static_cast<std::ptrdiff_t>(i + current_segment_length);
encrypted_segment.assign(segment_begin, segment_end);
encrypted_package_stream.read(
reinterpret_cast<char *>(encrypted_segment.data()),
encrypted_segment.size());
auto decrypted_segment = xlnt::detail::aes_cbc_decrypt(encrypted_segment, key, iv);
decrypted_segment.resize(current_segment_length);
decrypted_package.insert(
decrypted_package.end(),
@ -103,21 +105,15 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(
return decrypted_package;
}
encryption_info::standard_encryption_info read_standard_encryption_info(const std::vector<std::uint8_t> &info_bytes)
encryption_info::standard_encryption_info read_standard_encryption_info(std::istream &info_stream)
{
encryption_info::standard_encryption_info result;
auto reader = binary_reader<byte>(info_bytes);
// skip version info
reader.read<std::uint32_t>();
reader.read<std::uint32_t>();
auto header_length = reader.read<std::uint32_t>();
auto index_at_start = reader.offset();
/*auto skip_flags = */ reader.read<std::uint32_t>();
/*auto size_extra = */ reader.read<std::uint32_t>();
auto alg_id = reader.read<std::uint32_t>();
auto header_length = read<std::uint32_t>(info_stream);
auto index_at_start = info_stream.tellg();
/*auto skip_flags = */ read<std::uint32_t>(info_stream);
/*auto size_extra = */ read<std::uint32_t>(info_stream);
auto alg_id = read<std::uint32_t>(info_stream);
if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610)
{
@ -128,68 +124,50 @@ encryption_info::standard_encryption_info read_standard_encryption_info(const st
throw xlnt::exception("invalid cipher algorithm");
}
auto alg_id_hash = reader.read<std::uint32_t>();
auto alg_id_hash = read<std::uint32_t>(info_stream);
if (alg_id_hash != 0x00008004 && alg_id_hash == 0)
{
throw xlnt::exception("invalid hash algorithm");
}
result.key_bits = reader.read<std::uint32_t>();
result.key_bits = read<std::uint32_t>(info_stream);
result.key_bytes = result.key_bits / 8;
auto provider_type = reader.read<std::uint32_t>();
auto provider_type = read<std::uint32_t>(info_stream);
if (provider_type != 0 && provider_type != 0x00000018)
{
throw xlnt::exception("invalid provider type");
}
reader.read<std::uint32_t>(); // reserved 1
if (reader.read<std::uint32_t>() != 0) // reserved 2
read<std::uint32_t>(info_stream); // reserved 1
if (read<std::uint32_t>(info_stream) != 0) // reserved 2
{
throw xlnt::exception("invalid header");
}
const auto csp_name_length = header_length - (reader.offset() - index_at_start);
std::vector<std::uint16_t> csp_name_wide(
reinterpret_cast<const std::uint16_t *>(&*(info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset()))),
reinterpret_cast<const std::uint16_t *>(
&*(info_bytes.begin() + static_cast<std::ptrdiff_t>(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")
const auto csp_name_length = header_length - (info_stream.tellg() - index_at_start);
auto csp_name = xlnt::detail::read_string<char16_t>(info_stream, csp_name_length);
if (csp_name != u"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
&& csp_name != u"Microsoft Enhanced RSA and AES Cryptographic Provider")
{
throw xlnt::exception("invalid cryptographic provider");
}
reader.offset(reader.offset() + csp_name_length);
info_stream.seekg(csp_name_length);
const auto salt_size = reader.read<std::uint32_t>();
result.salt = std::vector<std::uint8_t>(
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset()),
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset() + salt_size));
reader.offset(reader.offset() + salt_size);
const auto salt_size = read<std::uint32_t>(info_stream);
result.salt = xlnt::detail::read_vector<byte>(info_stream, salt_size);
static const auto verifier_size = std::size_t(16);
result.encrypted_verifier = std::vector<std::uint8_t>(
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset()),
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset() + verifier_size));
reader.offset(reader.offset() + verifier_size);
result.encrypted_verifier = xlnt::detail::read_vector<byte>(info_stream, verifier_size);
/*const auto verifier_hash_size = */reader.read<std::uint32_t>();
/*const auto verifier_hash_size = */read<std::uint32_t>(info_stream);
const auto encrypted_verifier_hash_size = std::size_t(32);
result.encrypted_verifier_hash = std::vector<std::uint8_t>(
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset()),
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset() + encrypted_verifier_hash_size));
reader.offset(reader.offset() + encrypted_verifier_hash_size);
if (reader.offset() != info_bytes.size())
{
throw xlnt::exception("extra data after encryption info");
}
result.encrypted_verifier_hash = xlnt::detail::read_vector<byte>(info_stream, encrypted_verifier_hash_size);
return result;
}
encryption_info::agile_encryption_info read_agile_encryption_info(const std::vector<std::uint8_t> &info_bytes)
encryption_info::agile_encryption_info read_agile_encryption_info(std::istream &info_stream)
{
using xlnt::detail::decode_base64;
@ -199,8 +177,10 @@ encryption_info::agile_encryption_info read_agile_encryption_info(const std::vec
encryption_info::agile_encryption_info result;
auto header_size = std::size_t(8);
xml::parser parser(info_bytes.data() + header_size, info_bytes.size() - header_size, "EncryptionInfo");
auto xml_string = std::string(
std::istreambuf_iterator<char>(info_stream),
std::istreambuf_iterator<char>());
xml::parser parser(xml_string.data(), xml_string.size(), "EncryptionInfo");
parser.next_expect(xml::parser::event_type::start_element, xmlns, "encryption");
@ -269,17 +249,15 @@ encryption_info::agile_encryption_info read_agile_encryption_info(const std::vec
return result;
}
encryption_info read_encryption_info(const std::vector<std::uint8_t> &info_bytes, const std::u16string &password)
encryption_info read_encryption_info(std::istream &info_stream, const std::u16string &password)
{
encryption_info info;
info.password = password;
auto reader = binary_reader<byte>(info_bytes);
auto version_major = reader.read<std::uint16_t>();
auto version_minor = reader.read<std::uint16_t>();
auto encryption_flags = reader.read<std::uint32_t>();
auto version_major = read<std::uint16_t>(info_stream);
auto version_minor = read<std::uint16_t>(info_stream);
auto encryption_flags = read<std::uint32_t>(info_stream);
info.is_agile = version_major == 4 && version_minor == 4;
@ -290,7 +268,7 @@ encryption_info read_encryption_info(const std::vector<std::uint8_t> &info_bytes
throw xlnt::exception("bad header");
}
info.agile = read_agile_encryption_info(info_bytes);
info.agile = read_agile_encryption_info(info_stream);
}
else
{
@ -315,7 +293,7 @@ encryption_info read_encryption_info(const std::vector<std::uint8_t> &info_bytes
throw xlnt::exception("not an OOXML document");
}
info.standard = read_standard_encryption_info(info_bytes);
info.standard = read_standard_encryption_info(info_stream);
}
return info;
@ -330,15 +308,16 @@ std::vector<std::uint8_t> decrypt_xlsx(
throw xlnt::exception("empty file");
}
xlnt::detail::compound_document document(bytes);
xlnt::detail::vector_istreambuf buffer(bytes);
std::istream stream(&buffer);
xlnt::detail::compound_document document(stream);
auto encryption_info = read_encryption_info(
document.read_stream("EncryptionInfo"), password);
auto encrypted_package = document.read_stream("EncryptedPackage");
auto &encryption_info_stream = document.open_read_stream("EncryptionInfo");
auto encryption_info = read_encryption_info(encryption_info_stream, password);
return encryption_info.is_agile
? decrypt_xlsx_agile(encryption_info, encrypted_package)
: decrypt_xlsx_standard(encryption_info, encrypted_package);
? decrypt_xlsx_agile(encryption_info, document.open_read_stream("EncryptedPackage"))
: decrypt_xlsx_standard(encryption_info, document.open_read_stream("EncryptedPackage"));
}
} // namespace

View File

@ -105,8 +105,9 @@ encryption_info generate_encryption_info(const std::u16string &/*password*/)
return result;
}
std::vector<std::uint8_t> write_agile_encryption_info(
const encryption_info &info)
void write_agile_encryption_info(
const encryption_info &info,
std::ostream &info_stream)
{
static const auto &xmlns = xlnt::constants::ns("encryption");
static const auto &xmlns_p = xlnt::constants::ns("encryption-password");
@ -166,10 +167,10 @@ std::vector<std::uint8_t> write_agile_encryption_info(
serializer.end_element(xmlns, "encryption");
return encryption_info;
info_stream.write(reinterpret_cast<char *>(encryption_info.data()), encryption_info.size());
}
std::vector<std::uint8_t> write_standard_encryption_info(const encryption_info &info)
void write_standard_encryption_info(const encryption_info &info, std::ostream &info_stream)
{
auto result = std::vector<std::uint8_t>();
auto writer = xlnt::detail::binary_writer<std::uint8_t>(result);
@ -205,15 +206,15 @@ std::vector<std::uint8_t> write_standard_encryption_info(const encryption_info &
writer.write(std::uint32_t(20));
writer.append(info.standard.encrypted_verifier_hash);
return result;
info_stream.write(reinterpret_cast<char *>(result.data()), result.size());
}
std::vector<std::uint8_t> encrypt_xlsx_agile(
void encrypt_xlsx_agile(
const encryption_info &info,
const std::vector<std::uint8_t> &plaintext)
std::ostream &plaintext)
{
auto key = info.calculate_key();
/*
auto padded = plaintext;
padded.resize((plaintext.size() / 16 + (plaintext.size() % 16 == 0 ? 0 : 1)) * 16);
auto ciphertext = xlnt::detail::aes_ecb_encrypt(padded, key);
@ -221,16 +222,15 @@ std::vector<std::uint8_t> encrypt_xlsx_agile(
ciphertext.insert(ciphertext.begin(),
reinterpret_cast<const std::uint8_t *>(&length),
reinterpret_cast<const std::uint8_t *>(&length + sizeof(std::uint64_t)));
return ciphertext;
*/
}
std::vector<std::uint8_t> encrypt_xlsx_standard(
void encrypt_xlsx_standard(
const encryption_info &info,
const std::vector<std::uint8_t> &plaintext)
std::ostream &plaintext)
{
auto key = info.calculate_key();
/*
auto padded = plaintext;
padded.resize((plaintext.size() / 16 + (plaintext.size() % 16 == 0 ? 0 : 1)) * 16);
auto ciphertext = xlnt::detail::aes_ecb_encrypt(padded, key);
@ -238,8 +238,7 @@ std::vector<std::uint8_t> encrypt_xlsx_standard(
ciphertext.insert(ciphertext.begin(),
reinterpret_cast<const std::uint8_t *>(&length),
reinterpret_cast<const std::uint8_t *>(&length + sizeof(std::uint64_t)));
return ciphertext;
*/
}
std::vector<std::uint8_t> encrypt_xlsx(
@ -250,14 +249,20 @@ std::vector<std::uint8_t> encrypt_xlsx(
encryption_info.password = u"secret";
auto ciphertext = std::vector<std::uint8_t>();
xlnt::detail::compound_document document(ciphertext);
xlnt::detail::vector_ostreambuf buffer(ciphertext);
std::ostream stream(&buffer);
xlnt::detail::compound_document document(stream);
document.write_stream("EncryptionInfo", encryption_info.is_agile
? write_agile_encryption_info(encryption_info)
: write_standard_encryption_info(encryption_info));
document.write_stream("EncryptedPackage", encryption_info.is_agile
? encrypt_xlsx_agile(encryption_info, plaintext)
: encrypt_xlsx_standard(encryption_info, plaintext));
if (encryption_info.is_agile)
{
write_agile_encryption_info(encryption_info, document.open_write_stream("/EncryptionInfo"));
encrypt_xlsx_agile(encryption_info, document.open_write_stream("/EncryptedPackage"));
}
else
{
write_standard_encryption_info(encryption_info, document.open_write_stream("/EncryptionInfo"));
encrypt_xlsx_standard(encryption_info, document.open_write_stream("/EncryptedPackage"));
}
return ciphertext;
}

View File

@ -197,7 +197,7 @@ public:
strm.avail_in = 0;
strm.next_in = Z_NULL;
setg(in.data(), in.data(), in.data());
setg(in.data(), in.data(), in.data() + buffer_size);
setp(0, 0);
// skip the header

View File

@ -73,19 +73,6 @@ void print_summary()
int main()
{
std::ifstream file("C:/Users/Thomas/Development/xlnt/tests/data/6_encrypted_libre.xlsx", std::ios::binary);
const auto bytes2 = xlnt::detail::to_vector(file);
xlnt::detail::compound_document doc2(bytes2);
auto info = doc2.read_stream("/EncryptionInfo");
std::vector<std::uint8_t> bytes;
xlnt::detail::compound_document doc(bytes);
doc.write_stream("aaa", std::vector<std::uint8_t>(4095, 'a'));
doc.write_stream("bbb", std::vector<std::uint8_t>(4095, 'b'));
doc.write_stream("ccc", std::vector<std::uint8_t>(4095, 'c'));
std::ofstream file2("cd.xlsx", std::ios::binary);
xlnt::detail::to_stream(bytes, file2);
// cell
run_tests<cell_test_suite>();
run_tests<index_types_test_suite>();