mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
intermediate commit
This commit is contained in:
parent
e1bf1f6262
commit
e2f733f6e0
|
@ -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
|
||||
|
|
906
source/detail/cryptography/compound_document.cpp
Normal file
906
source/detail/cryptography/compound_document.cpp
Normal 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
|
58
source/detail/cryptography/compound_document.hpp
Normal file
58
source/detail/cryptography/compound_document.hpp
Normal 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
|
182
source/detail/cryptography/encryption_info.cpp
Normal file
182
source/detail/cryptography/encryption_info.cpp
Normal 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
|
|
@ -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
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user