intermediate commit

This commit is contained in:
Thomas Fussell 2017-04-21 19:52:02 -04:00
parent e1bf1f6262
commit e2f733f6e0
9 changed files with 1395 additions and 1844 deletions

View File

@ -21,6 +21,8 @@
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <cstdint>
#include <vector>
@ -31,7 +33,7 @@ using byte = std::uint8_t;
using byte_vector = std::vector<byte>;
template <typename T>
auto read_int(const byte_vector &raw_data, std::size_t &index)
T read_int(const byte_vector &raw_data, std::size_t &index)
{
auto result = *reinterpret_cast<const T *>(&raw_data[index]);
index += sizeof(T);
@ -39,6 +41,28 @@ auto read_int(const byte_vector &raw_data, std::size_t &index)
return result;
}
template <typename T>
void write_int(T value, byte_vector &raw_data, std::size_t &index)
{
*reinterpret_cast<T *>(&raw_data[index]) = value;
index += sizeof(T);
}
static inline void writeU16(std::uint8_t *ptr, std::uint16_t data)
{
ptr[0] = static_cast<std::uint8_t>(data & 0xff);
ptr[1] = static_cast<std::uint8_t>((data >> 8) & 0xff);
}
static inline void writeU32(std::uint8_t *ptr, std::uint32_t data)
{
ptr[0] = static_cast<std::uint8_t>(data & 0xff);
ptr[1] = static_cast<std::uint8_t>((data >> 8) & 0xff);
ptr[2] = static_cast<std::uint8_t>((data >> 16) & 0xff);
ptr[3] = static_cast<std::uint8_t>((data >> 24) & 0xff);
}
template <typename T>
byte *vector_byte(std::vector<T> &v, std::size_t offset)
{
@ -57,5 +81,22 @@ byte *last_byte(std::vector<T> &v)
return vector_byte(v, v.size());
}
template <typename InIt>
byte_vector to_bytes(InIt begin, InIt end)
{
byte_vector bytes;
for (auto i = begin; i != end; ++i)
{
auto c = *i;
bytes.insert(
bytes.end(),
reinterpret_cast<char *>(&c),
reinterpret_cast<char *>(&c) + sizeof(c));
}
return bytes;
}
} // namespace detail
} // namespace xlnt

View File

@ -0,0 +1,906 @@
/* POLE - Portable C++ library to access OLE Storage
Copyright (C) 2002-2007 Ariya Hidayat (ariya@kde.org).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <array>
#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <list>
#include <string>
#include <vector>
#include <detail/cryptography/compound_document.hpp>
#include <xlnt/utils/exceptions.hpp>
namespace {
struct header
{
std::array<std::uint8_t, 8> 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<std::uint32_t, 109> bb_blocks;
};
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::uint32_t>);
std::vector<std::size_t> follow(std::size_t start);
std::size_t operator[](std::size_t index);
void load(const std::vector<std::uint8_t> &data);
void save(std::vector<std::uint8_t> &data);
std::size_t size();
void debug();
private:
std::vector<std::uint32_t> data_;
};
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
};
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<std::size_t> children(std::size_t index);
void load(const std::vector<std::uint8_t> &data);
void save(std::vector<std::uint8_t> &data);
std::size_t size();
void debug();
private:
std::vector<directory_entry> 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<std::size_t> pre;
for (std::size_t i = 0; i < n; i++)
pre.push_back(unused());
}
std::size_t allocation_table::operator[](std::size_t index)
{
std::size_t result;
result = data_[index];
return result;
}
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<std::uint32_t> 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<std::size_t> &chain, std::size_t item)
{
for (std::size_t i = 0; i < chain.size(); i++)
if (chain[i] == item) return true;
return false;
}
// follow
std::vector<std::size_t> allocation_table::follow(std::size_t start)
{
std::vector<std::size_t> chain;
if (start >= count()) return chain;
std::size_t p = start;
while (p < count())
{
if (p == static_cast<std::size_t>(Eof)) break;
if (p == static_cast<std::size_t>(Bat)) break;
if (p == static_cast<std::size_t>(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<std::uint8_t> &data)
{
resize(data.size() / 4);
std::copy(
reinterpret_cast<const std::uint32_t *>(&data[0]),
reinterpret_cast<const std::uint32_t *>(&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<std::uint8_t> &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<std::ptrdiff_t>(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<std::size_t> chi = children(j);
for (std::size_t i = 0; i < chi.size(); i++)
if (chi[i] == index) return static_cast<std::ptrdiff_t>(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<std::size_t>(p));
if (_entry->dir && _entry->valid)
{
result.insert(0, _entry->name);
result.insert(0, "/");
}
--p;
index = static_cast<std::size_t>(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<std::string> 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<std::string>::iterator it;
for (it = names.begin(); it != names.end(); ++it)
{
// find among the children of index
std::vector<std::size_t> 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<std::ptrdiff_t>(chi[i]);
}
// traverse to the child
if (child > 0)
index = static_cast<std::size_t>(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<std::uint32_t>(index);
}
}
return entry(index);
}
// helper function: recursively find siblings of index
void dirtree_find_siblings(directory_tree *dirtree, std::vector<std::size_t> &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<std::size_t> directory_tree::children(std::size_t index)
{
std::vector<std::size_t> 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<std::uint8_t> &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<std::size_t>(xlnt::detail::read_int<std::uint16_t>(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<char>(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<std::uint32_t>(data, offset);
offset = 0x78 + p;
e.size = xlnt::detail::read_int<std::uint32_t>(data, offset);
offset = 0x44 + p;
e.prev = xlnt::detail::read_int<std::uint32_t>(data, offset);
offset = 0x48 + p;
e.next = xlnt::detail::read_int<std::uint32_t>(data, offset);
offset = 0x4C + p;
e.child = xlnt::detail::read_int<std::uint32_t>(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<std::uint8_t> &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<std::uint8_t>(entry_name[j]);
}
auto offset = std::size_t(0x40);
xlnt::detail::write_int(static_cast<std::uint16_t>(entry_name.length() * 2 + 2), data, offset);
data[0x42] = 5;
data[0x43] = 1;
xlnt::detail::write_int(0xffffffff, data, offset);
xlnt::detail::write_int(0xffffffff, data, offset);
xlnt::detail::write_int(0xffffffff, data, root->child);
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<std::uint8_t>(entry_name[j]);
}
offset = 0x40 + i * 128;
xlnt::detail::write_int(static_cast<std::uint16_t>(entry_name.length() * 2 + 2), data, offset);
xlnt::detail::write_int(static_cast<std::uint16_t>(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<std::uint8_t, 8> 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<std::uint8_t> &data)
{
header h;
auto in = &data[0];
auto offset = std::size_t(0x1e);
h.big_shift = xlnt::detail::read_int<std::uint16_t>(data, offset);
h.small_shift = xlnt::detail::read_int<std::uint16_t>(data, offset);
offset = 0x2c;
h.num_big_blocks = xlnt::detail::read_int<std::uint32_t>(data, offset);
h.directory_start = xlnt::detail::read_int<std::uint32_t>(data, offset);
offset = 0x38;
h.threshold = xlnt::detail::read_int<std::uint32_t>(data, offset);
h.small_start = xlnt::detail::read_int<std::uint32_t>(data, offset);
h.num_small_blocks = xlnt::detail::read_int<std::uint32_t>(data, offset);
h.meta_start = xlnt::detail::read_int<std::uint32_t>(data, offset);
h.num_meta_blocks = xlnt::detail::read_int<std::uint32_t>(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<std::uint32_t>(data, offset);
}
return h;
}
void save_header(const header &h, std::vector<std::uint8_t> &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<std::uint32_t>(0), out, offset);
xlnt::detail::write_int(static_cast<std::uint32_t>(0), out, offset);
xlnt::detail::write_int(static_cast<std::uint32_t>(0), out, offset);
xlnt::detail::write_int(static_cast<std::uint32_t>(0), out, offset);
xlnt::detail::write_int(static_cast<std::uint16_t>(0x003e), out, offset);
xlnt::detail::write_int(static_cast<std::uint16_t>(3), out, offset);
xlnt::detail::write_int(static_cast<std::uint16_t>(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<std::uint32_t>(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
{
std::size_t segment_length_;
byte_vector buffer_;
directory_tree directory_;
header header_;
allocation_table small_block_table_;
allocation_table big_block_table_;
std::vector<std::size_t> blocks_;
std::vector<std::size_t> sb_blocks_;
};
std::vector<std::uint8_t> load_small_blocks(compound_document_impl &d)
{
auto bytes = std::size_t(0);
std::vector<std::uint8_t> result;
for (std::size_t i = 0; i < d.blocks_.size(); i++)
{
std::size_t block = d.blocks_[i];
const auto block_size = d.small_block_table_.blockSize;
std::size_t pos = block_size * (block + 1);
std::size_t p = block_size;
if (pos + p > d.buffer_.size())
{
p = d.buffer_.size() - pos;
}
std::copy(d.buffer_.begin() + 0, d.buffer_.begin() + p, std::back_inserter(result));
bytes += p;
}
return result;
}
std::vector<std::uint8_t> load_big_blocks(const std::vector<std::uint32_t> &blocks, compound_document_impl &d)
{
std::vector<std::uint8_t> 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<std::uint8_t> load_big_block(std::uint32_t block, compound_document_impl &d)
{
return load_big_blocks({ block }, d);
}
compound_document::compound_document()
: d_(new compound_document_impl())
{
d_->header_ = initialize_header();
d_->big_block_table_.blockSize = static_cast<std::size_t>(1) << d_->header_.big_shift;
d_->small_block_table_.blockSize = static_cast<std::size_t>(1) << d_->header_.small_shift;
}
compound_document::~compound_document()
{
}
void compound_document::load(const std::vector<std::uint8_t> &data)
{
d_->buffer_ = data;
// 1. load header
{
if (data.size() < 512)
{
throw xlnt::exception("not ole");
}
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<std::size_t>(1) << d_->header_.big_shift;
d_->big_block_table_.blockSize = big_block_size;
auto small_block_size = static_cast<std::size_t>(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<std::size_t>(
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;
std::size_t mblock = d_->header_.meta_start;
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<std::uint32_t>(buffer, offset);
}
auto offset = big_block_size - 4;
mblock = xlnt::detail::read_int<std::uint32_t>(buffer, offset);
}
}
// load big block table
if (!blocks.empty())
{
d_->big_block_table_.load(load_big_blocks(blocks, *d_));
}
// load small bat
blocks.clear();
blocks = d_->big_block_table_.follow(d_->header_.small_start);
auto buflen = blocks.size() * big_block_size;
if (buflen > 0)
{
buffer.resize(buflen);
load_big_blocks(blocks, *d_);
d_->small_block_table_.load(buffer);
}
// load directory tree
blocks.clear();
blocks = d_->big_block_table_.follow(d_->header_.directory_start);
buflen = blocks.size() * big_block_size;
buffer.resize(buflen);
load_big_blocks(blocks, *d_);
d_->directory_.load(buffer);
auto offset = std::size_t(0x74);
auto sb_start = xlnt::detail::read_int<std::uint32_t>(buffer, offset);
// fetch block chain as data for small-files
d_->sb_blocks_ = d_->big_block_table_.follow(sb_start); // small files
}
std::vector<std::uint8_t> compound_document::save() const
{
return d_->buffer_;
}
bool compound_document::has_stream(const std::string &/*filename*/) const
{
return false;
}
void compound_document::add_stream(
const std::string &name,
const std::vector<std::uint8_t> &/*data*/)
{
d_->directory_.entry(name, !has_stream(name));
}
std::vector<std::uint8_t> compound_document::stream(const std::string &name) const
{
if (!has_stream(name))
{
throw xlnt::exception("document doesn't contain stream with the given name");
}
auto entry = d_->directory_.entry(name);
auto total_bytes = std::size_t(0);
auto pos = std::size_t(0);
byte_vector result;
if (entry->size < d_->header_.threshold)
{
// small file
auto block_size = d_->small_block_table_.blockSize;
auto index = pos / block_size;
auto buf = byte_vector(block_size, 0);
std::size_t offset = pos % block_size;
while (index < d_->blocks_.size())
{
load_small_blocks(*d_);
auto count = block_size - offset;
std::copy(d_->buffer_.begin() + total_bytes, d_->buffer_.begin() + count, buf.begin() + offset);
total_bytes += count;
offset = 0;
index++;
}
}
else
{
// big file
auto block_size = d_->small_block_table_.blockSize;
auto index = pos / block_size;
auto buf = byte_vector(block_size, 0);
std::size_t offset = pos % block_size;
while (index < d_->blocks_.size())
{
load_big_block(d_->blocks_[index], *d_);
auto count = block_size - offset;
std::copy(d_->buffer_.begin() + total_bytes, d_->buffer_.begin() + count, buf.begin() + offset);
total_bytes += count;
index++;
offset = 0;
}
}
return result;
}
std::size_t compound_document::segment_length() const
{
return d_->segment_length_;
}
} // namespace detail
} // namespace xlnt

View File

@ -0,0 +1,58 @@
/* POLE - Portable C++ library to access OLE Storage
Copyright (C) 2002-2007 Ariya Hidayat (ariya@kde.org).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <memory>
#include <string>
#include <detail/bytes.hpp>
namespace xlnt {
namespace detail {
struct compound_document_impl;
class compound_document
{
public:
compound_document();
~compound_document();
void load(const std::vector<std::uint8_t> &data);
std::vector<std::uint8_t> save() const;
bool has_stream(const std::string &filename) const;
void add_stream(const std::string &filename, const std::vector<std::uint8_t> &data);
std::vector<std::uint8_t> stream(const std::string &filename) const;
std::size_t segment_length() const;
private:
std::unique_ptr<compound_document_impl> d_;
};
} // namespace detail
} // namespace xlnt

View File

@ -0,0 +1,182 @@
// Copyright (c) 2017 Thomas Fussell
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#include <array>
#include <detail/bytes.hpp>
#include <detail/cryptography/aes.hpp>
#include <detail/cryptography/encryption_info.hpp>
namespace {
using xlnt::detail::encryption_info;
std::vector<std::uint8_t> calculate_standard_key(
encryption_info::standard_encryption_info info,
const std::u16string &password)
{
// H_0 = H(salt + password)
auto salt_plus_password = info.salt_value;
auto password_bytes = xlnt::detail::to_bytes(password.begin(), password.end());
std::copy(password_bytes.begin(),
password_bytes.end(),
std::back_inserter(salt_plus_password));
auto 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);
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 < info.spin_count; ++iterator)
{
hash(info.hash, iterator_plus_h_n, h_n);
std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4);
}
// H_final = H(H_n + block)
auto h_n_plus_block = h_n;
const std::uint32_t block_number = 0;
h_n_plus_block.insert(
h_n_plus_block.end(),
reinterpret_cast<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);
// X1 = H(h_final ^ 0x36)
std::vector<std::uint8_t> buffer(64, 0x36);
for (std::size_t i = 0; i < h_final.size(); ++i)
{
buffer[i] = static_cast<std::uint8_t>(0x36 ^ h_final[i]);
}
auto X1 = hash(info.hash, buffer);
// X2 = H(h_final ^ 0x5C)
buffer.assign(64, 0x5c);
for (std::size_t i = 0; i < h_final.size(); ++i)
{
buffer[i] = static_cast<std::uint8_t>(0x5c ^ h_final[i]);
}
auto X2 = hash(info.hash, buffer);
auto X3 = X1;
X3.insert(X3.end(), X2.begin(), X2.end());
auto key = std::vector<std::uint8_t>(X3.begin(),
X3.begin() + static_cast<std::ptrdiff_t>(info.key_bytes));
using xlnt::detail::aes_ecb_decrypt;
//TODO: check these variables
auto calculated_verifier_hash = hash(info.hash,
aes_ecb_decrypt(info.verifier_hash_input, key));
auto decrypted_verifier_hash = aes_ecb_decrypt(
info.verifier_hash_value, key);
decrypted_verifier_hash.resize(info.verifier_hash_input.size());
if (calculated_verifier_hash != decrypted_verifier_hash)
{
throw xlnt::exception("bad password");
}
return key;
}
std::vector<std::uint8_t> calculate_agile_key(
encryption_info::agile_encryption_info info,
const std::u16string &password)
{
// 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());
std::copy(password_bytes.begin(),
password_bytes.end(),
std::back_inserter(salt_plus_password));
auto h_0 = hash(info.key_encryptor.hash, 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 < info.key_encryptor.spin_count; ++iterator)
{
hash(info.key_encryptor.hash, iterator_plus_h_n, h_n);
std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4);
}
static const std::size_t block_size = 8;
auto calculate_block = [&info](
const std::vector<std::uint8_t> &raw_key,
const std::array<std::uint8_t, block_size> &block,
const std::vector<std::uint8_t> &encrypted)
{
auto combined = raw_key;
combined.insert(combined.end(), block.begin(), block.end());
auto key = hash(info.key_encryptor.hash, combined);
key.resize(info.key_encryptor.key_bits / 8);
using xlnt::detail::aes_cbc_decrypt;
return aes_cbc_decrypt(encrypted, key, info.key_encryptor.salt_value);
};
const std::array<std::uint8_t, block_size> input_block_key = { { 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79 } };
auto hash_input = calculate_block(h_n, input_block_key, info.key_encryptor.verifier_hash_input);
auto calculated_verifier = hash(info.key_encryptor.hash, hash_input);
const std::array<std::uint8_t, block_size> verifier_block_key = {
{ 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e } };
auto expected_verifier = calculate_block(h_n, verifier_block_key, info.key_encryptor.verifier_hash_value);
expected_verifier.resize(calculated_verifier.size());
if (calculated_verifier != expected_verifier)
{
throw xlnt::exception("bad password");
}
const std::array<std::uint8_t, block_size> key_value_block_key =
{
{ 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6 }
};
return calculate_block(h_n, key_value_block_key, info.key_encryptor.encrypted_key_value);
}
} // namespace
namespace xlnt {
namespace detail {
std::vector<std::uint8_t> encryption_info::calculate_key() const
{
return is_agile
? calculate_agile_key(agile, password)
: calculate_standard_key(standard, password);
}
} // namespace detail
} // namespace xlnt

View File

@ -90,6 +90,8 @@ struct encryption_info
std::vector<std::uint8_t> encrypted_key_value;
} key_encryptor;
} agile;
std::vector<std::uint8_t> calculate_key() const;
};
} // namespace detail

File diff suppressed because it is too large Load Diff

View File

@ -1,344 +0,0 @@
/* POLE - Portable C++ library to access OLE Storage
Copyright (C) 2002-2007 Ariya Hidayat (ariya@kde.org).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <cstdint>
#include <iostream>
#include <list>
#include <string>
#include <fstream>
#include <string>
#include <vector>
namespace POLE
{
const std::size_t OleSegmentLength = 4096;
class StorageIO;
class Stream;
class StreamIO;
class DirTree;
class DirEntry;
class Storage
{
friend class Stream;
friend class StreamOut;
public:
// for Storage::result()
enum { Ok, OpenFailed, NotOLE, BadOLE, UnknownError };
/**
* Constructs a storage with name filename.
**/
Storage(std::uint8_t *bytes, std::size_t length);
/**
* Destroys the storage.
**/
~Storage();
/**
* Opens the storage. Returns true if no error occurs.
**/
bool open();
/**
* Closes the storage.
**/
void close();
/**
* Returns the error code of last operation.
**/
int result();
/**
* Finds all stream and directories in given path.
**/
std::list<std::string> entries( const std::string& path = "/" );
/**
* Returns true if specified entry name is a directory.
*/
bool isDirectory( const std::string& name );
DirTree* dirTree();
StorageIO* storageIO();
std::list<DirEntry *> dirEntries( const std::string& path = "/" );
std::vector<std::uint8_t> file(const std::string &name);
private:
StorageIO* io;
// no copy or assign
Storage( const Storage& );
Storage& operator=( const Storage& );
};
class Stream
{
friend class Storage;
friend class StorageIO;
public:
/**
* Creates a new stream.
*/
// name must be absolute, e.g "/Workbook"
Stream( Storage* storage, const std::string& name );
/**
* Destroys the stream.
*/
~Stream();
/**
* Returns the full stream name.
*/
std::string fullName();
/**
* Returns the stream size.
**/
std::size_t size();
/**
* Returns the current read/write position.
**/
std::size_t tell();
/**
* Sets the read/write position.
**/
void seek( std::size_t pos );
/**
* Reads a byte.
**/
int getch();
/**
* Reads a block of data.
**/
std::size_t read( std::uint8_t* data, std::size_t maxlen );
/**
* Returns true if the read/write position is past the file.
**/
bool eof();
/**
* Returns true whenever error occurs.
**/
bool fail();
private:
StreamIO* io;
// no copy or assign
Stream( const Stream& );
Stream& operator=( const Stream& );
};
class Header
{
public:
std::uint8_t id[8]; // signature, or magic identifier
std::uint16_t b_shift; // bbat->blockSize = 1 << b_shift
std::uint16_t s_shift; // sbat->blockSize = 1 << s_shift
std::uint32_t num_bat; // blocks allocated for big bat
std::uint32_t dirent_start; // starting block for directory info
std::uint32_t threshold; // switch from small to big file (usually 4K)
std::uint32_t sbat_start; // starting block index to store small bat
std::uint32_t num_sbat; // blocks allocated for small bat
std::uint32_t mbat_start; // starting block to store meta bat
std::uint32_t num_mbat; // blocks allocated for meta bat
std::uint32_t bb_blocks[109];
Header();
bool valid();
void load( const std::uint8_t* buffer );
void save( std::uint8_t* buffer );
void debug();
};
class AllocTable
{
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;
AllocTable();
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::uint32_t> );
std::vector<std::size_t> follow( std::size_t start );
std::size_t operator[](std::size_t index );
void load( const std::uint8_t* buffer, std::size_t len );
void save( std::uint8_t* buffer );
std::size_t size();
void debug();
private:
std::vector<std::uint32_t> data;
AllocTable( const AllocTable& );
AllocTable& operator=( const AllocTable& );
};
class DirEntry
{
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
DirEntry(): valid(false), name(), dir(false), size(0), start(0),
prev(0), next(0), child(0) {}
};
class DirTree
{
public:
static const std::uint32_t End;
DirTree();
void clear();
std::size_t entryCount();
DirEntry* entry( std::size_t index );
DirEntry* entry( const std::string& name, bool create=false );
std::ptrdiff_t indexOf( DirEntry* e );
std::ptrdiff_t parent( std::size_t index );
std::string fullName( std::size_t index );
std::vector<std::size_t> children( std::size_t index );
void load( std::uint8_t* buffer, std::size_t len );
void save( std::uint8_t* buffer );
std::size_t size();
void debug();
private:
std::vector<DirEntry> entries;
DirTree( const DirTree& );
DirTree& operator=( const DirTree& );
};
class StorageIO
{
public:
Storage* storage; // owner
std::uint8_t *filedata;
std::size_t dataLength;
int result; // result of operation
bool opened; // true if file is opened
std::size_t filesize; // size of the file
Header* header; // storage header
DirTree* dirtree; // directory tree
AllocTable* bbat; // allocation table for big blocks
AllocTable* sbat; // allocation table for small blocks
std::vector<std::size_t> sb_blocks; // blocks for "small" files
std::list<Stream*> streams;
StorageIO( Storage* storage, std::uint8_t *bytes, std::size_t length );
~StorageIO();
bool open();
void close();
void flush();
void load();
void create();
std::size_t loadBigBlocks( std::vector<std::size_t> blocks, std::uint8_t* buffer, std::size_t maxlen );
std::size_t loadBigBlock( std::size_t block, std::uint8_t* buffer, std::size_t maxlen );
std::size_t loadSmallBlocks( std::vector<std::size_t> blocks, std::uint8_t* buffer, std::size_t maxlen );
std::size_t loadSmallBlock( std::size_t block, std::uint8_t* buffer, std::size_t maxlen );
StreamIO* streamIO( const std::string& name );
private:
// no copy or assign
StorageIO( const StorageIO& );
StorageIO& operator=( const StorageIO& );
};
class StreamIO
{
public:
StorageIO* io;
DirEntry* entry;
std::string fullName;
bool eof;
bool fail;
StreamIO( StorageIO* io, DirEntry* entry );
~StreamIO();
std::size_t size();
void seek( std::size_t pos );
std::size_t tell();
int getch();
std::size_t read( std::uint8_t* data, std::size_t maxlen );
std::size_t read( std::size_t pos, std::uint8_t* data, std::size_t maxlen );
private:
std::vector<std::size_t> blocks;
// no copy or assign
StreamIO( const StreamIO& );
StreamIO& operator=( const StreamIO& );
// pointer for read
std::size_t m_pos;
// simple cache system to speed-up getch()
std::uint8_t* cache_data;
std::size_t cache_size;
std::size_t cache_pos;
void updateCache();
};
} // namespace POLE

View File

@ -31,7 +31,7 @@
#include <detail/cryptography/encryption_info.hpp>
#include <detail/cryptography/aes.hpp>
#include <detail/cryptography/base64.hpp>
#include <detail/cryptography/pole.hpp>
#include <detail/cryptography/compound_document.hpp>
#include <detail/cryptography/value_traits.hpp>
#include <detail/external/include_libstudxml.hpp>
#include <detail/serialization/vector_streambuf.hpp>
@ -45,57 +45,112 @@ using xlnt::detail::byte_vector;
using xlnt::detail::encryption_info;
std::vector<std::uint8_t> decrypt_xlsx_standard(
const byte_vector &encryption_info,
const std::u16string &password,
encryption_info info,
const byte_vector &encrypted_package)
{
std::size_t offset = 0;
const auto key = info.calculate_key();
encryption_info::standard_encryption_info info;
auto offset = std::size_t(0);
auto decrypted_size = xlnt::detail::read_int<std::uint64_t>(encrypted_package, offset);
auto decrypted = xlnt::detail::aes_ecb_decrypt(encrypted_package, key, offset);
decrypted.resize(static_cast<std::size_t>(decrypted_size));
return decrypted;
}
std::vector<std::uint8_t> decrypt_xlsx_agile(
const encryption_info &info,
const std::vector<std::uint8_t> &encrypted_package,
const std::size_t segment_length)
{
const auto key = info.calculate_key();
auto salt_size = info.agile.key_data.salt_size;
auto salt_with_block_key = info.agile.key_data.salt_value;
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()));
std::vector<std::uint8_t> encrypted_segment(segment_length, 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)
{
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);
auto decrypted_segment = xlnt::detail::aes_cbc_decrypt(encrypted_segment, key, iv);
decrypted_segment.resize(current_segment_length);
decrypted_package.insert(
decrypted_package.end(),
decrypted_segment.begin(),
decrypted_segment.end());
++segment;
}
decrypted_package.resize(total_size);
return decrypted_package;
}
encryption_info read_standard_encryption_info(const std::vector<std::uint8_t> &info_bytes)
{
encryption_info result;
result.is_agile = false;
auto &standard_info = result.standard;
using xlnt::detail::read_int;
auto offset = std::size_t(0);
auto header_length = read_int<std::uint32_t>(encryption_info, offset);
auto header_length = read_int<std::uint32_t>(info_bytes, offset);
auto index_at_start = offset;
/*auto skip_flags = */ read_int<std::uint32_t>(encryption_info, offset);
/*auto size_extra = */ read_int<std::uint32_t>(encryption_info, offset);
auto alg_id = read_int<std::uint32_t>(encryption_info, offset);
/*auto skip_flags = */ read_int<std::uint32_t>(info_bytes, offset);
/*auto size_extra = */ read_int<std::uint32_t>(info_bytes, offset);
auto alg_id = read_int<std::uint32_t>(info_bytes, offset);
if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610)
{
info.cipher = xlnt::detail::cipher_algorithm::aes;
standard_info.cipher = xlnt::detail::cipher_algorithm::aes;
}
else
{
throw xlnt::exception("invalid cipher algorithm");
}
auto alg_id_hash = read_int<std::uint32_t>(encryption_info, offset);
auto alg_id_hash = read_int<std::uint32_t>(info_bytes, offset);
if (alg_id_hash != 0x00008004 && alg_id_hash == 0)
{
throw xlnt::exception("invalid hash algorithm");
}
info.key_bits = read_int<std::uint32_t>(encryption_info, offset);
info.key_bytes = info.key_bits / 8;
standard_info.key_bits = read_int<std::uint32_t>(info_bytes, offset);
standard_info.key_bytes = standard_info.key_bits / 8;
auto provider_type = read_int<std::uint32_t>(encryption_info, offset);
auto provider_type = read_int<std::uint32_t>(info_bytes, offset);
if (provider_type != 0 && provider_type != 0x00000018)
{
throw xlnt::exception("invalid provider type");
}
read_int<std::uint32_t>(encryption_info, offset); // reserved 1
if (read_int<std::uint32_t>(encryption_info, offset) != 0) // reserved 2
read_int<std::uint32_t>(info_bytes, offset); // reserved 1
if (read_int<std::uint32_t>(info_bytes, offset) != 0) // reserved 2
{
throw xlnt::exception("invalid header");
}
const auto csp_name_length = header_length - (offset - index_at_start);
std::vector<std::uint16_t> csp_name_wide(
reinterpret_cast<const std::uint16_t *>(&*(encryption_info.begin() + static_cast<std::ptrdiff_t>(offset))),
reinterpret_cast<const std::uint16_t *>(&*(info_bytes.begin() + static_cast<std::ptrdiff_t>(offset))),
reinterpret_cast<const std::uint16_t *>(
&*(encryption_info.begin() + static_cast<std::ptrdiff_t>(offset + csp_name_length))));
&*(info_bytes.begin() + static_cast<std::ptrdiff_t>(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")
@ -104,106 +159,31 @@ std::vector<std::uint8_t> decrypt_xlsx_standard(
}
offset += csp_name_length;
const auto salt_size = read_int<std::uint32_t>(encryption_info, offset);
std::vector<std::uint8_t> salt(encryption_info.begin() + static_cast<std::ptrdiff_t>(offset),
encryption_info.begin() + static_cast<std::ptrdiff_t>(offset + salt_size));
const auto salt_size = read_int<std::uint32_t>(info_bytes, offset);
std::vector<std::uint8_t> salt(info_bytes.begin() + static_cast<std::ptrdiff_t>(offset),
info_bytes.begin() + static_cast<std::ptrdiff_t>(offset + salt_size));
offset += salt_size;
static const auto verifier_size = std::size_t(16);
std::vector<std::uint8_t> encrypted_verifier(encryption_info.begin() + static_cast<std::ptrdiff_t>(offset),
encryption_info.begin() + static_cast<std::ptrdiff_t>(offset + verifier_size));
std::vector<std::uint8_t> encrypted_verifier(info_bytes.begin() + static_cast<std::ptrdiff_t>(offset),
info_bytes.begin() + static_cast<std::ptrdiff_t>(offset + verifier_size));
offset += verifier_size;
const auto verifier_hash_size = read_int<std::uint32_t>(encryption_info, offset);
const auto verifier_hash_size = read_int<std::uint32_t>(info_bytes, offset);
const auto encrypted_verifier_hash_size = std::size_t(32);
std::vector<std::uint8_t> encrypted_verifier_hash(encryption_info.begin() + static_cast<std::ptrdiff_t>(offset),
encryption_info.begin() + static_cast<std::ptrdiff_t>(offset + encrypted_verifier_hash_size));
std::vector<std::uint8_t> encrypted_verifier_hash(info_bytes.begin() + static_cast<std::ptrdiff_t>(offset),
info_bytes.begin() + static_cast<std::ptrdiff_t>(offset + encrypted_verifier_hash_size));
offset += encrypted_verifier_hash_size;
if (offset != encryption_info.size())
if (offset != info_bytes.size())
{
throw xlnt::exception("extra data after encryption info");
}
// begin key generation algorithm
// H_0 = H(salt + password)
auto salt_plus_password = salt;
std::for_each(password.begin(), password.end(), [&salt_plus_password](char16_t c) {
salt_plus_password.insert(
salt_plus_password.end(),
reinterpret_cast<char *>(&c),
reinterpret_cast<char *>(&c) + sizeof(char16_t));
});
auto 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);
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 < info.spin_count; ++iterator)
{
hash(info.hash, iterator_plus_h_n, h_n);
std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4);
}
// H_final = H(H_n + block)
auto h_n_plus_block = h_n;
const std::uint32_t block_number = 0;
h_n_plus_block.insert(
h_n_plus_block.end(),
reinterpret_cast<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);
// X1 = H(h_final ^ 0x36)
std::vector<std::uint8_t> buffer(64, 0x36);
for (std::size_t i = 0; i < h_final.size(); ++i)
{
buffer[i] = static_cast<std::uint8_t>(0x36 ^ h_final[i]);
}
auto X1 = hash(info.hash, buffer);
// X2 = H(h_final ^ 0x5C)
buffer.assign(64, 0x5c);
for (std::size_t i = 0; i < h_final.size(); ++i)
{
buffer[i] = static_cast<std::uint8_t>(0x5c ^ h_final[i]);
}
auto X2 = hash(info.hash, buffer);
auto X3 = X1;
X3.insert(X3.end(), X2.begin(), X2.end());
auto key = std::vector<std::uint8_t>(X3.begin(),
X3.begin() + static_cast<std::ptrdiff_t>(info.key_bytes));
using xlnt::detail::aes_ecb_decrypt;
auto calculated_verifier_hash = hash(info.hash,
aes_ecb_decrypt(encrypted_verifier, key));
auto decrypted_verifier_hash = aes_ecb_decrypt(
encrypted_verifier_hash, key);
decrypted_verifier_hash.resize(verifier_hash_size);
if (calculated_verifier_hash != decrypted_verifier_hash)
{
throw xlnt::exception("bad password");
}
offset = 0;
auto decrypted_size = read_int<std::uint64_t>(encrypted_package, offset);
auto decrypted = aes_ecb_decrypt(encrypted_package, key, offset);
decrypted.resize(static_cast<std::size_t>(decrypted_size));
return decrypted;
return result;
}
std::vector<std::uint8_t> decrypt_xlsx_agile(
const std::vector<std::uint8_t> &encryption_info,
const std::u16string &password,
const std::vector<std::uint8_t> &encrypted_package)
encryption_info read_agile_encryption_info(const std::vector<std::uint8_t> &info_bytes)
{
using xlnt::detail::decode_base64;
@ -211,26 +191,28 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(
static const auto &xmlns_p = xlnt::constants::ns("encryption-password");
// static const auto &xmlns_c = xlnt::constants::namespace_("encryption-certificate");
encryption_info::agile_encryption_info result;
encryption_info result;
result.is_agile = true;
auto &agile_info = result.agile;
xml::parser parser(encryption_info.data(), encryption_info.size(), "EncryptionInfo");
xml::parser parser(info_bytes.data(), info_bytes.size(), "EncryptionInfo");
parser.next_expect(xml::parser::event_type::start_element, xmlns, "encryption");
parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyData");
result.key_data.salt_size = parser.attribute<std::size_t>("saltSize");
result.key_data.block_size = parser.attribute<std::size_t>("blockSize");
result.key_data.key_bits = parser.attribute<std::size_t>("keyBits");
result.key_data.hash_size = parser.attribute<std::size_t>("hashSize");
result.key_data.cipher_algorithm = parser.attribute("cipherAlgorithm");
result.key_data.cipher_chaining = parser.attribute("cipherChaining");
result.key_data.hash_algorithm = parser.attribute("hashAlgorithm");
result.key_data.salt_value = decode_base64(parser.attribute("saltValue"));
agile_info.key_data.salt_size = parser.attribute<std::size_t>("saltSize");
agile_info.key_data.block_size = parser.attribute<std::size_t>("blockSize");
agile_info.key_data.key_bits = parser.attribute<std::size_t>("keyBits");
agile_info.key_data.hash_size = parser.attribute<std::size_t>("hashSize");
agile_info.key_data.cipher_algorithm = parser.attribute("cipherAlgorithm");
agile_info.key_data.cipher_chaining = parser.attribute("cipherChaining");
agile_info.key_data.hash_algorithm = parser.attribute("hashAlgorithm");
agile_info.key_data.salt_value = decode_base64(parser.attribute("saltValue"));
parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyData");
parser.next_expect(xml::parser::event_type::start_element, xmlns, "dataIntegrity");
result.data_integrity.hmac_key = decode_base64(parser.attribute("encryptedHmacKey"));
result.data_integrity.hmac_value = decode_base64(parser.attribute("encryptedHmacValue"));
agile_info.data_integrity.hmac_key = decode_base64(parser.attribute("encryptedHmacKey"));
agile_info.data_integrity.hmac_value = decode_base64(parser.attribute("encryptedHmacValue"));
parser.next_expect(xml::parser::event_type::end_element, xmlns, "dataIntegrity");
parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptors");
@ -245,21 +227,21 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(
if (parser.namespace_() == xmlns_p && parser.name() == "encryptedKey")
{
any_password_key = true;
result.key_encryptor.spin_count = parser.attribute<std::size_t>("spinCount");
result.key_encryptor.salt_size = parser.attribute<std::size_t>("saltSize");
result.key_encryptor.block_size = parser.attribute<std::size_t>("blockSize");
result.key_encryptor.key_bits = parser.attribute<std::size_t>("keyBits");
result.key_encryptor.hash_size = parser.attribute<std::size_t>("hashSize");
result.key_encryptor.cipher_algorithm = parser.attribute("cipherAlgorithm");
result.key_encryptor.cipher_chaining = parser.attribute("cipherChaining");
result.key_encryptor.hash = parser.attribute<xlnt::detail::hash_algorithm>("hashAlgorithm");
result.key_encryptor.salt_value =
agile_info.key_encryptor.spin_count = parser.attribute<std::size_t>("spinCount");
agile_info.key_encryptor.salt_size = parser.attribute<std::size_t>("saltSize");
agile_info.key_encryptor.block_size = parser.attribute<std::size_t>("blockSize");
agile_info.key_encryptor.key_bits = parser.attribute<std::size_t>("keyBits");
agile_info.key_encryptor.hash_size = parser.attribute<std::size_t>("hashSize");
agile_info.key_encryptor.cipher_algorithm = parser.attribute("cipherAlgorithm");
agile_info.key_encryptor.cipher_chaining = parser.attribute("cipherChaining");
agile_info.key_encryptor.hash = parser.attribute<xlnt::detail::hash_algorithm>("hashAlgorithm");
agile_info.key_encryptor.salt_value =
decode_base64(parser.attribute("saltValue"));
result.key_encryptor.verifier_hash_input =
agile_info.key_encryptor.verifier_hash_input =
decode_base64(parser.attribute("encryptedVerifierHashInput"));
result.key_encryptor.verifier_hash_value =
agile_info.key_encryptor.verifier_hash_value =
decode_base64(parser.attribute("encryptedVerifierHashValue"));
result.key_encryptor.encrypted_key_value =
agile_info.key_encryptor.encrypted_key_value =
decode_base64(parser.attribute("encryptedKeyValue"));
}
else
@ -280,133 +262,19 @@ std::vector<std::uint8_t> decrypt_xlsx_agile(
parser.next_expect(xml::parser::event_type::end_element, xmlns, "encryption");
// begin key generation algorithm
// H_0 = H(salt + password)
auto salt_plus_password = result.key_encryptor.salt_value;
std::for_each(password.begin(), password.end(), [&salt_plus_password](std::uint16_t c) {
salt_plus_password.insert(salt_plus_password.end(), reinterpret_cast<char *>(&c),
reinterpret_cast<char *>(&c) + sizeof(std::uint16_t));
});
auto h_0 = hash(result.key_encryptor.hash, salt_plus_password);
// H_n = H(iterator + H_n-1)
std::vector<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)
{
hash(result.key_encryptor.hash, iterator_plus_h_n, h_n);
std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4);
}
static const std::size_t block_size = 8;
auto calculate_block = [&result](
const std::vector<std::uint8_t> &raw_key,
const std::array<std::uint8_t, block_size> &block,
const std::vector<std::uint8_t> &encrypted)
{
auto combined = raw_key;
combined.insert(combined.end(), block.begin(), block.end());
auto key = hash(result.key_encryptor.hash, combined);
key.resize(result.key_encryptor.key_bits / 8);
using xlnt::detail::aes_cbc_decrypt;
return aes_cbc_decrypt(encrypted, key, result.key_encryptor.salt_value);
};
const std::array<std::uint8_t, block_size> input_block_key = {{0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79}};
auto hash_input = calculate_block(h_n, input_block_key, result.key_encryptor.verifier_hash_input);
auto calculated_verifier = hash(result.key_encryptor.hash, hash_input);
const std::array<std::uint8_t, block_size> verifier_block_key = {
{0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e}};
auto expected_verifier = calculate_block(h_n, verifier_block_key, result.key_encryptor.verifier_hash_value);
expected_verifier.resize(calculated_verifier.size());
if (calculated_verifier != expected_verifier)
{
throw xlnt::exception("bad password");
}
const std::array<std::uint8_t, block_size> key_value_block_key =
{
{0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6}
};
auto key = calculate_block(h_n, key_value_block_key, result.key_encryptor.encrypted_key_value);
auto salt_size = result.key_data.salt_size;
auto salt_with_block_key = result.key_data.salt_value;
salt_with_block_key.resize(salt_size + sizeof(std::uint32_t), 0);
auto &segment = *reinterpret_cast<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()));
std::vector<std::uint8_t> encrypted_segment(POLE::OleSegmentLength, 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 += POLE::OleSegmentLength)
{
auto iv = hash(result.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(POLE::OleSegmentLength, 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);
auto decrypted_segment = xlnt::detail::aes_cbc_decrypt(encrypted_segment, key, iv);
decrypted_segment.resize(current_segment_length);
decrypted_package.insert(
decrypted_package.end(),
decrypted_segment.begin(),
decrypted_segment.end());
++segment;
}
decrypted_package.resize(total_size);
return decrypted_package;
return result;
}
std::vector<std::uint8_t> decrypt_xlsx(
const std::vector<std::uint8_t> &bytes,
const std::u16string &password)
encryption_info read_encryption_info(const std::vector<std::uint8_t> &info_bytes)
{
if (bytes.empty())
{
throw xlnt::exception("empty file");
}
POLE::Storage storage(const_cast<std::uint8_t *>(bytes.data()), bytes.size());
if (!storage.open())
{
throw xlnt::exception("not an ole compound file");
}
auto encrypted_package = storage.file("EncryptedPackage");
auto encryption_info = storage.file("EncryptionInfo");
encryption_info info;
using xlnt::detail::read_int;
std::size_t offset = 0;
auto version_major = read_int<std::uint16_t>(encryption_info, offset);
auto version_minor = read_int<std::uint16_t>(encryption_info, offset);
auto encryption_flags = read_int<std::uint32_t>(encryption_info, offset);
// get rid of header
encryption_info.erase(
encryption_info.begin(),
encryption_info.begin() + static_cast<std::ptrdiff_t>(offset));
auto version_major = read_int<std::uint16_t>(info_bytes, offset);
auto version_minor = read_int<std::uint16_t>(info_bytes, offset);
auto encryption_flags = read_int<std::uint32_t>(info_bytes, offset);
// version 4.4 is agile
if (version_major == 4 && version_minor == 4)
@ -416,7 +284,7 @@ std::vector<std::uint8_t> decrypt_xlsx(
throw xlnt::exception("bad header");
}
return decrypt_xlsx_agile(encryption_info, password, encrypted_package);
return read_agile_encryption_info(info_bytes);
}
// not agile, only try to decrypt versions 3.2 and 4.2
@ -441,7 +309,29 @@ std::vector<std::uint8_t> decrypt_xlsx(
throw xlnt::exception("not an OOXML document");
}
return decrypt_xlsx_standard(encryption_info, password, encrypted_package);
return read_standard_encryption_info(info_bytes);
}
std::vector<std::uint8_t> decrypt_xlsx(
const std::vector<std::uint8_t> &bytes,
const std::u16string &password)
{
if (bytes.empty())
{
throw xlnt::exception("empty file");
}
xlnt::detail::compound_document document;
document.load(bytes);
auto encryption_info = read_encryption_info(document.stream("EncryptionInfo"));
encryption_info.password = password;
auto encrypted_package = document.stream("EncryptedPackage");
auto segment_length = document.segment_length();
return encryption_info.is_agile
? decrypt_xlsx_agile(encryption_info, encrypted_package, segment_length)
: decrypt_xlsx_standard(encryption_info, encrypted_package);
}
} // namespace

View File

@ -23,7 +23,9 @@
#include <detail/constants.hpp>
#include <detail/unicode.hpp>
#include <detail/cryptography/aes.hpp>
#include <detail/cryptography/base64.hpp>
#include <detail/cryptography/compound_document.hpp>
#include <detail/cryptography/encryption_info.hpp>
#include <detail/cryptography/value_traits.hpp>
#include <detail/external/include_libstudxml.hpp>
@ -76,7 +78,7 @@ encryption_info generate_encryption_info(const std::u16string &password)
}
std::vector<std::uint8_t> write_agile_encryption_info(
const encryption_info::agile_encryption_info &info)
const encryption_info &info)
{
static const auto &xmlns = xlnt::constants::ns("encryption");
static const auto &xmlns_p = xlnt::constants::ns("encryption-password");
@ -88,38 +90,48 @@ std::vector<std::uint8_t> write_agile_encryption_info(
serializer.start_element(xmlns, "encryption");
const auto key_data = info.agile.key_data;
serializer.start_element(xmlns, "keyData");
serializer.attribute("saltSize", info.key_data.salt_size);
serializer.attribute("blockSize", info.key_data.block_size);
serializer.attribute("keyBits", info.key_data.key_bits);
serializer.attribute("hashSize", info.key_data.hash_size);
serializer.attribute("cipherAlgorithm", info.key_data.cipher_algorithm);
serializer.attribute("cipherChaining", info.key_data.cipher_chaining);
serializer.attribute("hashAlgorithm", info.key_data.hash_algorithm);
serializer.attribute("saltValue", xlnt::detail::encode_base64(info.key_data.salt_value));
serializer.attribute("saltSize", key_data.salt_size);
serializer.attribute("blockSize", key_data.block_size);
serializer.attribute("keyBits", key_data.key_bits);
serializer.attribute("hashSize", key_data.hash_size);
serializer.attribute("cipherAlgorithm", key_data.cipher_algorithm);
serializer.attribute("cipherChaining", key_data.cipher_chaining);
serializer.attribute("hashAlgorithm", key_data.hash_algorithm);
serializer.attribute("saltValue",
xlnt::detail::encode_base64(key_data.salt_value));
serializer.end_element(xmlns, "keyData");
const auto data_integrity = info.agile.data_integrity;
serializer.start_element(xmlns, "dataIntegrity");
serializer.attribute("encryptedHmacKey", xlnt::detail::encode_base64(info.data_integrity.hmac_key));
serializer.attribute("encryptedHmacValue", xlnt::detail::encode_base64(info.data_integrity.hmac_value));
serializer.attribute("encryptedHmacKey",
xlnt::detail::encode_base64(data_integrity.hmac_key));
serializer.attribute("encryptedHmacValue",
xlnt::detail::encode_base64(data_integrity.hmac_value));
serializer.end_element(xmlns, "dataIntegrity");
const auto key_encryptor = info.agile.key_encryptor;
serializer.start_element(xmlns, "keyEncryptors");
serializer.start_element(xmlns, "keyEncryptor");
serializer.attribute("uri", "");
serializer.start_element(xmlns_p, "encryptedKey");
serializer.attribute("spinCount", info.key_encryptor.spin_count);
serializer.attribute("saltSize", info.key_encryptor.salt_size);
serializer.attribute("blockSize", info.key_encryptor.block_size);
serializer.attribute("keyBits", info.key_encryptor.key_bits);
serializer.attribute("hashSize", info.key_encryptor.hash_size);
serializer.attribute("cipherAlgorithm", info.key_encryptor.cipher_algorithm);
serializer.attribute("cipherChaining", info.key_encryptor.cipher_chaining);
serializer.attribute("hashAlgorithm", info.key_encryptor.hash);
serializer.attribute("saltValue", xlnt::detail::encode_base64(info.key_encryptor.salt_value));
serializer.attribute("encryptedVerifierHashInput", xlnt::detail::encode_base64(info.key_encryptor.verifier_hash_input));
serializer.attribute("encryptedVerifierHashValue", xlnt::detail::encode_base64(info.key_encryptor.verifier_hash_value));
serializer.attribute("encryptedKeyValue", xlnt::detail::encode_base64(info.key_encryptor.encrypted_key_value));
serializer.attribute("spinCount", key_encryptor.spin_count);
serializer.attribute("saltSize", key_encryptor.salt_size);
serializer.attribute("blockSize", key_encryptor.block_size);
serializer.attribute("keyBits", key_encryptor.key_bits);
serializer.attribute("hashSize", key_encryptor.hash_size);
serializer.attribute("cipherAlgorithm", key_encryptor.cipher_algorithm);
serializer.attribute("cipherChaining", key_encryptor.cipher_chaining);
serializer.attribute("hashAlgorithm", key_encryptor.hash);
serializer.attribute("saltValue",
xlnt::detail::encode_base64(key_encryptor.salt_value));
serializer.attribute("encryptedVerifierHashInput",
xlnt::detail::encode_base64(key_encryptor.verifier_hash_input));
serializer.attribute("encryptedVerifierHashValue",
xlnt::detail::encode_base64(key_encryptor.verifier_hash_value));
serializer.attribute("encryptedKeyValue",
xlnt::detail::encode_base64(key_encryptor.encrypted_key_value));
serializer.end_element(xmlns_p, "encryptedKey");
serializer.end_element(xmlns, "keyEncryptor");
serializer.end_element(xmlns, "keyEncryptors");
@ -130,35 +142,43 @@ std::vector<std::uint8_t> write_agile_encryption_info(
}
std::vector<std::uint8_t> write_standard_encryption_info(
const encryption_info::standard_encryption_info &/*info*/)
const encryption_info &/*info*/)
{
return {};
}
std::vector<std::uint8_t> write_encryption_info(const encryption_info &info)
std::vector<std::uint8_t> encrypt_xlsx_agile(
const encryption_info &/*info*/,
const std::vector<std::uint8_t> &/*plaintext*/)
{
return (info.is_agile)
? write_agile_encryption_info(info.agile)
: write_standard_encryption_info(info.standard);
return {};
}
std::vector<std::uint8_t> write_encryption_info(const std::u16string &password)
std::vector<std::uint8_t> encrypt_xlsx_standard(
const encryption_info &/*info*/,
const std::vector<std::uint8_t> &/*plaintext*/)
{
return write_encryption_info(generate_encryption_info(password));
//auto key = info.calculate_key();
return {};
}
std::vector<std::uint8_t> encrypt_xlsx(
const std::vector<std::uint8_t> &bytes,
const std::vector<std::uint8_t> &plaintext,
const std::u16string &password)
{
if (bytes.empty())
{
throw xlnt::exception("empty file");
}
auto encryption_info = generate_encryption_info(password);
write_encryption_info(password);
xlnt::detail::compound_document document;
return {};
document.add_stream("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
? encrypt_xlsx_agile(encryption_info, plaintext)
: encrypt_xlsx_standard(encryption_info, plaintext));
return document.save();
}
} // namespace
@ -167,24 +187,21 @@ namespace xlnt {
namespace detail {
std::vector<std::uint8_t> XLNT_API encrypt_xlsx(
const std::vector<std::uint8_t> &data,
const std::vector<std::uint8_t> &plaintext,
const std::string &password)
{
return ::encrypt_xlsx(data, utf8_to_utf16(password));
return ::encrypt_xlsx(plaintext, utf8_to_utf16(password));
}
void xlsx_producer::write(std::ostream &destination, const std::string &password)
{
std::vector<std::uint8_t> decrypted;
std::vector<std::uint8_t> plaintext;
vector_ostreambuf plaintext_buffer(plaintext);
std::ostream decrypted_stream(&plaintext_buffer);
write(decrypted_stream);
{
vector_ostreambuf decrypted_buffer(decrypted);
std::ostream decrypted_stream(&decrypted_buffer);
write(decrypted_stream);
}
const auto encrypted = ::encrypt_xlsx(decrypted, utf8_to_utf16(password));
vector_istreambuf encrypted_buffer(encrypted);
const auto ciphertext = ::encrypt_xlsx(plaintext, utf8_to_utf16(password));
vector_istreambuf encrypted_buffer(ciphertext);
destination << &encrypted_buffer;
}