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
3a57242b68
commit
89858e32b3
|
@ -30,73 +30,230 @@ namespace xlnt {
|
|||
namespace detail {
|
||||
|
||||
using byte = std::uint8_t;
|
||||
using byte_vector = std::vector<byte>;
|
||||
|
||||
template <typename T>
|
||||
T read_int(const byte_vector &raw_data, std::size_t &index)
|
||||
class byte_reader
|
||||
{
|
||||
auto result = *reinterpret_cast<const T *>(&raw_data[index]);
|
||||
index += sizeof(T);
|
||||
public:
|
||||
byte_reader() = delete;
|
||||
|
||||
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)
|
||||
{
|
||||
return reinterpret_cast<byte *>(v.data() + offset);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
byte *first_byte(std::vector<T> &v)
|
||||
{
|
||||
return vector_byte(v, 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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)
|
||||
byte_reader(const std::vector<byte> &bytes)
|
||||
: bytes_(&bytes)
|
||||
{
|
||||
auto c = *i;
|
||||
bytes.insert(
|
||||
bytes.end(),
|
||||
reinterpret_cast<char *>(&c),
|
||||
reinterpret_cast<char *>(&c) + sizeof(c));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
byte_reader &operator=(const byte_reader &other)
|
||||
{
|
||||
offset_ = other.offset_;
|
||||
bytes_ = other.bytes_;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~byte_reader()
|
||||
{
|
||||
}
|
||||
|
||||
const std::vector<std::uint8_t> &data() const
|
||||
{
|
||||
return *bytes_;
|
||||
}
|
||||
|
||||
void offset(std::size_t offset)
|
||||
{
|
||||
offset_ = offset;
|
||||
}
|
||||
|
||||
std::size_t offset() const
|
||||
{
|
||||
return offset_;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
offset_ = 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T read()
|
||||
{
|
||||
T result;
|
||||
std::memcpy(&result, bytes_->data() + offset_, sizeof(T));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> as_vector_of() const
|
||||
{
|
||||
auto result = std::vector<T>(size() / sizeof(T), 0);
|
||||
std::memcpy(result.data(), bytes_->data(), size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return bytes_->size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t offset_ = 0;
|
||||
const std::vector<std::uint8_t> *bytes_;
|
||||
};
|
||||
|
||||
class byte_vector
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
static byte_vector from(const std::vector<T> &ints)
|
||||
{
|
||||
byte_vector result;
|
||||
|
||||
result.resize(ints.size() / sizeof(T));
|
||||
std::memcpy(result.bytes_.data(), ints.data(), result.bytes_.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static byte_vector from(const std::basic_string<T> &string)
|
||||
{
|
||||
byte_vector result;
|
||||
|
||||
result.resize(string.size() / sizeof(T));
|
||||
std::memcpy(result.bytes_.data(), string.data(), result.bytes_.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
byte_vector()
|
||||
: reader_(bytes_)
|
||||
{
|
||||
}
|
||||
|
||||
byte_vector(std::vector<byte> &bytes)
|
||||
: bytes_(bytes),
|
||||
reader_(bytes_)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
byte_vector(const std::vector<T> &ints)
|
||||
: byte_vector()
|
||||
{
|
||||
bytes_ = from(ints).data();
|
||||
}
|
||||
|
||||
byte_vector(const byte_vector &other)
|
||||
: byte_vector()
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
~byte_vector()
|
||||
{
|
||||
}
|
||||
|
||||
byte_vector &operator=(const byte_vector &other)
|
||||
{
|
||||
bytes_ = other.bytes_;
|
||||
reader_ = byte_reader(bytes_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::vector<byte> &data() const
|
||||
{
|
||||
return bytes_;
|
||||
}
|
||||
|
||||
std::vector<byte> data()
|
||||
{
|
||||
return bytes_;
|
||||
}
|
||||
|
||||
void data(std::vector<byte> &bytes)
|
||||
{
|
||||
bytes_ = bytes;
|
||||
}
|
||||
|
||||
void offset(std::size_t offset)
|
||||
{
|
||||
reader_.offset(offset);
|
||||
}
|
||||
|
||||
std::size_t offset() const
|
||||
{
|
||||
return reader_.offset();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
reader_.reset();
|
||||
bytes_.clear();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T read()
|
||||
{
|
||||
return reader_.read<T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> as_vector_of() const
|
||||
{
|
||||
return reader_.as_vector_of<T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write(T value)
|
||||
{
|
||||
const auto num_bytes = sizeof(T);
|
||||
|
||||
if (offset() + num_bytes > size())
|
||||
{
|
||||
extend(offset() + num_bytes - size());
|
||||
}
|
||||
|
||||
std::memcpy(bytes_.data() + offset(), &value, num_bytes);
|
||||
reader_.offset(reader_.offset() + num_bytes);
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return bytes_.size();
|
||||
}
|
||||
|
||||
void resize(std::size_t new_size, byte fill = 0)
|
||||
{
|
||||
bytes_.resize(new_size, fill);
|
||||
}
|
||||
|
||||
void extend(std::size_t amount, byte fill = 0)
|
||||
{
|
||||
bytes_.resize(size() + amount, fill);
|
||||
}
|
||||
|
||||
std::vector<byte>::iterator iterator()
|
||||
{
|
||||
return bytes_.begin() + offset();
|
||||
}
|
||||
|
||||
void append(const std::vector<std::uint8_t> &data, std::size_t offset, std::size_t count)
|
||||
{
|
||||
extend(count);
|
||||
std::memcpy(bytes_.data(), data.data() + offset, count);
|
||||
}
|
||||
|
||||
void append(const std::vector<std::uint8_t> &data)
|
||||
{
|
||||
append(data, 0, data.size());
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<byte> bytes_;
|
||||
byte_reader reader_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace xlnt
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -33,7 +33,7 @@
|
|||
namespace xlnt {
|
||||
namespace detail {
|
||||
|
||||
struct compound_document_impl;
|
||||
class compound_document_impl;
|
||||
|
||||
class compound_document
|
||||
{
|
||||
|
@ -41,14 +41,17 @@ public:
|
|||
compound_document();
|
||||
~compound_document();
|
||||
|
||||
void load(const std::vector<std::uint8_t> &data);
|
||||
void load(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;
|
||||
bool has_stream(const std::u16string &filename) const;
|
||||
void add_stream(const std::u16string &filename, const std::vector<std::uint8_t> &data);
|
||||
std::vector<std::uint8_t> stream(const std::u16string &filename) const;
|
||||
|
||||
private:
|
||||
compound_document_impl &impl();
|
||||
//TODO: can this return a const reference?
|
||||
compound_document_impl &impl() const;
|
||||
std::unique_ptr<compound_document_impl> d_;
|
||||
};
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ std::vector<std::uint8_t> calculate_standard_key(
|
|||
{
|
||||
// H_0 = H(salt + password)
|
||||
auto salt_plus_password = info.salt;
|
||||
auto password_bytes = xlnt::detail::to_bytes(password.begin(), password.end());
|
||||
auto password_bytes = xlnt::detail::byte_vector::from(password).data();
|
||||
std::copy(password_bytes.begin(),
|
||||
password_bytes.end(),
|
||||
std::back_inserter(salt_plus_password));
|
||||
|
@ -107,7 +107,7 @@ std::vector<std::uint8_t> calculate_agile_key(
|
|||
{
|
||||
// H_0 = H(salt + password)
|
||||
auto salt_plus_password = info.key_encryptor.salt_value;
|
||||
auto password_bytes = xlnt::detail::to_bytes(password.begin(), password.end());
|
||||
auto password_bytes = xlnt::detail::byte_vector::from(password).data();
|
||||
std::copy(password_bytes.begin(),
|
||||
password_bytes.end(),
|
||||
std::back_inserter(salt_plus_password));
|
||||
|
|
|
@ -46,13 +46,13 @@ using xlnt::detail::encryption_info;
|
|||
|
||||
std::vector<std::uint8_t> decrypt_xlsx_standard(
|
||||
encryption_info info,
|
||||
const byte_vector &encrypted_package)
|
||||
const std::vector<std::uint8_t> &encrypted_package)
|
||||
{
|
||||
const auto key = info.calculate_key();
|
||||
|
||||
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);
|
||||
auto reader = xlnt::detail::byte_reader(encrypted_package);
|
||||
auto decrypted_size = reader.read<std::uint64_t>();
|
||||
auto decrypted = xlnt::detail::aes_ecb_decrypt(encrypted_package, key, reader.offset());
|
||||
decrypted.resize(static_cast<std::size_t>(decrypted_size));
|
||||
|
||||
return decrypted;
|
||||
|
@ -108,14 +108,17 @@ encryption_info read_standard_encryption_info(const std::vector<std::uint8_t> &i
|
|||
result.is_agile = false;
|
||||
auto &standard_info = result.standard;
|
||||
|
||||
using xlnt::detail::read_int;
|
||||
auto offset = std::size_t(8); // skip version info
|
||||
auto reader = xlnt::detail::byte_reader(info_bytes);
|
||||
|
||||
auto header_length = read_int<std::uint32_t>(info_bytes, offset);
|
||||
auto index_at_start = 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);
|
||||
// skip version info
|
||||
reader.read<std::uint32_t>();
|
||||
reader.read<std::uint32_t>();
|
||||
|
||||
auto header_length = reader.read<std::uint32_t>();
|
||||
auto index_at_start = reader.offset();
|
||||
/*auto skip_flags = */ reader.read<std::uint32_t>();
|
||||
/*auto size_extra = */ reader.read<std::uint32_t>();
|
||||
auto alg_id = reader.read<std::uint32_t>();
|
||||
|
||||
if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610)
|
||||
{
|
||||
|
@ -126,60 +129,59 @@ encryption_info read_standard_encryption_info(const std::vector<std::uint8_t> &i
|
|||
throw xlnt::exception("invalid cipher algorithm");
|
||||
}
|
||||
|
||||
auto alg_id_hash = read_int<std::uint32_t>(info_bytes, offset);
|
||||
auto alg_id_hash = reader.read<std::uint32_t>();
|
||||
if (alg_id_hash != 0x00008004 && alg_id_hash == 0)
|
||||
{
|
||||
throw xlnt::exception("invalid hash algorithm");
|
||||
}
|
||||
|
||||
standard_info.key_bits = read_int<std::uint32_t>(info_bytes, offset);
|
||||
standard_info.key_bits = reader.read<std::uint32_t>();
|
||||
standard_info.key_bytes = standard_info.key_bits / 8;
|
||||
|
||||
auto provider_type = read_int<std::uint32_t>(info_bytes, offset);
|
||||
auto provider_type = reader.read<std::uint32_t>();
|
||||
if (provider_type != 0 && provider_type != 0x00000018)
|
||||
{
|
||||
throw xlnt::exception("invalid provider type");
|
||||
}
|
||||
|
||||
read_int<std::uint32_t>(info_bytes, offset); // reserved 1
|
||||
if (read_int<std::uint32_t>(info_bytes, offset) != 0) // reserved 2
|
||||
reader.read<std::uint32_t>(); // reserved 1
|
||||
if (reader.read<std::uint32_t>() != 0) // reserved 2
|
||||
{
|
||||
throw xlnt::exception("invalid header");
|
||||
}
|
||||
|
||||
const auto csp_name_length = header_length - (offset - index_at_start);
|
||||
const auto csp_name_length = header_length - (reader.offset() - index_at_start);
|
||||
std::vector<std::uint16_t> csp_name_wide(
|
||||
reinterpret_cast<const std::uint16_t *>(&*(info_bytes.begin() + static_cast<std::ptrdiff_t>(offset))),
|
||||
reinterpret_cast<const std::uint16_t *>(&*(info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset()))),
|
||||
reinterpret_cast<const std::uint16_t *>(
|
||||
&*(info_bytes.begin() + static_cast<std::ptrdiff_t>(offset + csp_name_length))));
|
||||
&*(info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset() + csp_name_length))));
|
||||
std::string csp_name(csp_name_wide.begin(), csp_name_wide.end() - 1); // without trailing null
|
||||
if (csp_name != "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
|
||||
&& csp_name != "Microsoft Enhanced RSA and AES Cryptographic Provider")
|
||||
{
|
||||
throw xlnt::exception("invalid cryptographic provider");
|
||||
}
|
||||
offset += csp_name_length;
|
||||
|
||||
const auto salt_size = read_int<std::uint32_t>(info_bytes, offset);
|
||||
const auto salt_size = reader.read<std::uint32_t>();
|
||||
standard_info.salt = std::vector<std::uint8_t>(
|
||||
info_bytes.begin() + static_cast<std::ptrdiff_t>(offset),
|
||||
info_bytes.begin() + static_cast<std::ptrdiff_t>(offset + salt_size));
|
||||
offset += salt_size;
|
||||
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset()),
|
||||
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset() + salt_size));
|
||||
reader.offset(reader.offset() + salt_size);
|
||||
|
||||
static const auto verifier_size = std::size_t(16);
|
||||
standard_info.encrypted_verifier = std::vector<std::uint8_t>(
|
||||
info_bytes.begin() + static_cast<std::ptrdiff_t>(offset),
|
||||
info_bytes.begin() + static_cast<std::ptrdiff_t>(offset + verifier_size));
|
||||
offset += verifier_size;
|
||||
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset()),
|
||||
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset() + verifier_size));
|
||||
reader.offset(reader.offset() + verifier_size);
|
||||
|
||||
const auto verifier_hash_size = read_int<std::uint32_t>(info_bytes, offset);
|
||||
const auto verifier_hash_size = reader.read<std::uint32_t>();
|
||||
const auto encrypted_verifier_hash_size = std::size_t(32);
|
||||
standard_info.encrypted_verifier_hash = std::vector<std::uint8_t>(
|
||||
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;
|
||||
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset()),
|
||||
info_bytes.begin() + static_cast<std::ptrdiff_t>(reader.offset() + encrypted_verifier_hash_size));
|
||||
reader.offset(reader.offset() + encrypted_verifier_hash_size);
|
||||
|
||||
if (offset != info_bytes.size())
|
||||
if (reader.offset() != info_bytes.size())
|
||||
{
|
||||
throw xlnt::exception("extra data after encryption info");
|
||||
}
|
||||
|
@ -274,12 +276,11 @@ encryption_info read_encryption_info(const std::vector<std::uint8_t> &info_bytes
|
|||
{
|
||||
encryption_info info;
|
||||
|
||||
using xlnt::detail::read_int;
|
||||
std::size_t offset = 0;
|
||||
auto reader = xlnt::detail::byte_reader(info_bytes);
|
||||
|
||||
auto version_major = read_int<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);
|
||||
auto version_major = reader.read<std::uint16_t>();
|
||||
auto version_minor = reader.read<std::uint16_t>();
|
||||
auto encryption_flags = reader.read<std::uint32_t>();
|
||||
|
||||
// version 4.4 is agile
|
||||
if (version_major == 4 && version_minor == 4)
|
||||
|
@ -327,11 +328,11 @@ std::vector<std::uint8_t> decrypt_xlsx(
|
|||
}
|
||||
|
||||
xlnt::detail::compound_document document;
|
||||
document.load(bytes);
|
||||
document.load(const_cast<std::vector<std::uint8_t> &>(bytes));
|
||||
|
||||
auto encryption_info = read_encryption_info(document.stream("EncryptionInfo"));
|
||||
auto encryption_info = read_encryption_info(document.stream(u"EncryptionInfo"));
|
||||
encryption_info.password = password;
|
||||
auto encrypted_package = document.stream("EncryptedPackage");
|
||||
auto encrypted_package = document.stream(u"EncryptedPackage");
|
||||
|
||||
return encryption_info.is_agile
|
||||
? decrypt_xlsx_agile(encryption_info, encrypted_package)
|
||||
|
|
|
@ -171,10 +171,10 @@ std::vector<std::uint8_t> encrypt_xlsx(
|
|||
|
||||
xlnt::detail::compound_document document;
|
||||
|
||||
document.add_stream("EncryptionInfo", encryption_info.is_agile
|
||||
document.add_stream(u"EncryptionInfo", encryption_info.is_agile
|
||||
? write_agile_encryption_info(encryption_info)
|
||||
: write_standard_encryption_info(encryption_info));
|
||||
document.add_stream("EncryptedPackage", encryption_info.is_agile
|
||||
document.add_stream(u"EncryptedPackage", encryption_info.is_agile
|
||||
? encrypt_xlsx_agile(encryption_info, plaintext)
|
||||
: encrypt_xlsx_standard(encryption_info, plaintext));
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user