Branch data TLA Line data Source code
1 : : // Copyright (c) 2014-2022 Thomas Fussell
2 : : // Copyright (c) 2024-2025 xlnt-community
3 : : //
4 : : // Permission is hereby granted, free of charge, to any person obtaining a copy
5 : : // of this software and associated documentation files (the "Software"), to deal
6 : : // in the Software without restriction, including without limitation the rights
7 : : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : : // copies of the Software, and to permit persons to whom the Software is
9 : : // furnished to do so, subject to the following conditions:
10 : : //
11 : : // The above copyright notice and this permission notice shall be included in
12 : : // all copies or substantial portions of the Software.
13 : : //
14 : : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : : // THE SOFTWARE
21 : : //
22 : : // @license: http://www.opensource.org/licenses/mit-license.php
23 : : // @author: see AUTHORS file
24 : :
25 : : #include <cstdint>
26 : : #include <vector>
27 : :
28 : : #include <xlnt/utils/exceptions.hpp>
29 : : #include <detail/binary.hpp>
30 : : #include <detail/constants.hpp>
31 : : #include <detail/cryptography/aes.hpp>
32 : : #include <detail/cryptography/base64.hpp>
33 : : #include <detail/cryptography/compound_document.hpp>
34 : : #include <detail/cryptography/encryption_info.hpp>
35 : : #include <detail/cryptography/value_traits.hpp>
36 : : #include <detail/cryptography/xlsx_crypto_consumer.hpp>
37 : : #include <detail/external/include_libstudxml.hpp>
38 : : #include <detail/serialization/vector_streambuf.hpp>
39 : : #include <detail/serialization/xlsx_consumer.hpp>
40 : : #include <detail/unicode.hpp>
41 : : #include <detail/utils/string_helpers.hpp>
42 : :
43 : : namespace {
44 : :
45 : : using xlnt::detail::byte;
46 : : using xlnt::detail::encryption_info;
47 : : using xlnt::detail::read;
48 : :
49 :CBC 15 : std::vector<std::uint8_t> decrypt_xlsx_standard(
50 : : encryption_info info,
51 : : std::istream &encrypted_package_stream)
52 : : {
53 [ + ]: 15 : const auto key = info.calculate_key();
54 : :
55 [ + ]: 12 : auto decrypted_size = read<std::uint64_t>(encrypted_package_stream);
56 : :
57 [ + ]: 12 : std::vector<std::uint8_t> encrypted_segment(4096, 0);
58 : 12 : std::vector<std::uint8_t> decrypted_package;
59 : :
60 [ + + + ]: 38 : while (encrypted_package_stream)
61 : : {
62 [ + ]: 26 : encrypted_package_stream.read(
63 : 26 : reinterpret_cast<char *>(encrypted_segment.data()),
64 : 26 : static_cast<std::streamsize>(encrypted_segment.size()));
65 [ + ]: 26 : auto decrypted_segment = xlnt::detail::aes_ecb_decrypt(encrypted_segment, key);
66 : :
67 [ + ]: 52 : decrypted_package.insert(
68 : 26 : decrypted_package.end(),
69 : : decrypted_segment.begin(),
70 : : decrypted_segment.end());
71 : 26 : }
72 : :
73 [ + ]: 12 : decrypted_package.resize(static_cast<std::size_t>(decrypted_size));
74 : :
75 : 12 : return decrypted_package;
76 : 12 : }
77 : :
78 : 6 : std::vector<std::uint8_t> decrypt_xlsx_agile(
79 : : const encryption_info &info,
80 : : std::istream &encrypted_package_stream)
81 : : {
82 [ + ]: 6 : const auto key = info.calculate_key();
83 : :
84 : 4 : auto salt_size = info.agile.key_data.salt_size;
85 [ + ]: 4 : auto salt_with_block_key = info.agile.key_data.salt_value;
86 [ + ]: 4 : salt_with_block_key.resize(salt_size + sizeof(std::uint32_t), 0);
87 : :
88 : 4 : auto &segment = *reinterpret_cast<std::uint32_t *>(salt_with_block_key.data() + salt_size);
89 [ + ]: 4 : auto total_size = read<std::uint64_t>(encrypted_package_stream);
90 : :
91 [ + ]: 4 : std::vector<std::uint8_t> encrypted_segment(4096, 0);
92 : 4 : std::vector<std::uint8_t> decrypted_package;
93 : :
94 [ + + + ]: 252 : while (encrypted_package_stream)
95 : : {
96 [ + ]: 248 : auto iv = hash(info.agile.key_encryptor.hash, salt_with_block_key);
97 [ + ]: 248 : iv.resize(16);
98 : :
99 [ + ]: 248 : encrypted_package_stream.read(
100 : 248 : reinterpret_cast<char *>(encrypted_segment.data()),
101 : 248 : static_cast<std::streamsize>(encrypted_segment.size()));
102 [ + ]: 248 : auto decrypted_segment = xlnt::detail::aes_cbc_decrypt(encrypted_segment, key, iv);
103 : :
104 [ + ]: 496 : decrypted_package.insert(
105 : 248 : decrypted_package.end(),
106 : : decrypted_segment.begin(),
107 : : decrypted_segment.end());
108 : :
109 : 248 : ++segment;
110 : 248 : }
111 : :
112 [ + ]: 4 : decrypted_package.resize(static_cast<std::size_t>(total_size));
113 : :
114 : 4 : return decrypted_package;
115 : 4 : }
116 : :
117 : 15 : encryption_info::standard_encryption_info read_standard_encryption_info(std::istream &info_stream)
118 : : {
119 : 15 : encryption_info::standard_encryption_info result;
120 : :
121 [ + ]: 15 : auto header_length = read<std::uint32_t>(info_stream);
122 [ + ]: 15 : auto index_at_start = info_stream.tellg();
123 [ + ]: 15 : /*auto skip_flags = */ read<std::uint32_t>(info_stream);
124 [ + ]: 15 : /*auto size_extra = */ read<std::uint32_t>(info_stream);
125 [ + ]: 15 : auto alg_id = read<std::uint32_t>(info_stream);
126 : :
127 [ + - - + : 15 : if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610)
- - - - ]
128 : : {
129 : 15 : result.cipher = xlnt::detail::cipher_algorithm::aes;
130 : : }
131 : : else
132 : : {
133 [ # # ]:UBC 0 : throw xlnt::exception("invalid cipher algorithm");
134 : : }
135 : :
136 [ + ]:CBC 15 : auto alg_id_hash = read<std::uint32_t>(info_stream);
137 [ - + - - ]: 15 : if (alg_id_hash != 0x00008004 && alg_id_hash == 0)
138 : : {
139 [ # # ]:UBC 0 : throw xlnt::exception("invalid hash algorithm");
140 : : }
141 : :
142 [ + ]:CBC 15 : result.key_bits = read<std::uint32_t>(info_stream);
143 : 15 : result.key_bytes = result.key_bits / 8;
144 : :
145 [ + ]: 15 : auto provider_type = read<std::uint32_t>(info_stream);
146 [ + - - + ]: 15 : if (provider_type != 0 && provider_type != 0x00000018)
147 : : {
148 [ # # ]:UBC 0 : throw xlnt::exception("invalid provider type");
149 : : }
150 : :
151 [ + ]:CBC 15 : read<std::uint32_t>(info_stream); // reserved 1
152 [ + - + ]: 15 : if (read<std::uint32_t>(info_stream) != 0) // reserved 2
153 : : {
154 [ # # ]:UBC 0 : throw xlnt::exception("invalid header");
155 : : }
156 : :
157 :CBC 15 : const auto csp_name_length = static_cast<std::size_t>((header_length
158 [ + ]: 15 : - (info_stream.tellg() - index_at_start))
159 : 15 : / 2);
160 [ + ]: 15 : auto csp_name = xlnt::detail::read_string<char16_t>(info_stream, csp_name_length);
161 : 15 : csp_name.pop_back(); // remove extraneous trailing null
162 : 15 : if (csp_name != u"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
163 [ + - - + : 15 : && csp_name != u"Microsoft Enhanced RSA and AES Cryptographic Provider")
- + ]
164 : : {
165 [ # # ]:UBC 0 : throw xlnt::exception("invalid cryptographic provider");
166 : : }
167 : :
168 [ + ]:CBC 15 : const auto salt_size = read<std::uint32_t>(info_stream);
169 [ + ]: 15 : result.salt = xlnt::detail::read_vector<byte>(info_stream, salt_size);
170 : :
171 : : static const auto verifier_size = std::size_t(16);
172 [ + ]: 15 : result.encrypted_verifier = xlnt::detail::read_vector<byte>(info_stream, verifier_size);
173 : :
174 [ + ]: 15 : /*const auto verifier_hash_size = */ read<std::uint32_t>(info_stream);
175 : 15 : const auto encrypted_verifier_hash_size = std::size_t(32);
176 [ + ]: 15 : result.encrypted_verifier_hash = xlnt::detail::read_vector<byte>(info_stream, encrypted_verifier_hash_size);
177 : :
178 : 15 : return result;
179 : 15 : }
180 : :
181 : 6 : encryption_info::agile_encryption_info read_agile_encryption_info(std::istream &info_stream)
182 : : {
183 : : using xlnt::detail::decode_base64;
184 : :
185 [ + + + - : 8 : static const auto &xmlns = xlnt::constants::ns("encryption");
+ + - - ]
186 [ + + + - : 8 : static const auto &xmlns_p = xlnt::constants::ns("encryption-password");
+ + - - ]
187 : : // static const auto &xmlns_c = xlnt::constants::namespace_("encryption-certificate");
188 : :
189 : 6 : encryption_info::agile_encryption_info result;
190 : :
191 [ + + ]: 12 : xml::parser parser(info_stream, "EncryptionInfo");
192 : :
193 [ + + ]: 6 : parser.next_expect(xml::parser::event_type::start_element, xmlns, "encryption");
194 : :
195 : 6 : auto &key_data = result.key_data;
196 [ + + ]: 12 : parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyData");
197 [ + + ]: 12 : key_data.salt_size = parser.attribute<std::size_t>("saltSize");
198 [ + + ]: 12 : key_data.block_size = parser.attribute<std::size_t>("blockSize");
199 [ + + ]: 12 : key_data.key_bits = parser.attribute<std::size_t>("keyBits");
200 [ + + ]: 12 : key_data.hash_size = parser.attribute<std::size_t>("hashSize");
201 [ + + + ]: 12 : key_data.cipher_algorithm = parser.attribute("cipherAlgorithm");
202 [ + + + ]: 12 : key_data.cipher_chaining = parser.attribute("cipherChaining");
203 [ + + + ]: 12 : key_data.hash_algorithm = parser.attribute("hashAlgorithm");
204 [ + + + ]: 12 : key_data.salt_value = decode_base64(parser.attribute("saltValue"));
205 [ + + ]: 6 : parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyData");
206 : :
207 : 6 : auto &data_integrity = result.data_integrity;
208 [ + + ]: 12 : parser.next_expect(xml::parser::event_type::start_element, xmlns, "dataIntegrity");
209 [ + + + ]: 12 : data_integrity.hmac_key = decode_base64(parser.attribute("encryptedHmacKey"));
210 [ + + + ]: 12 : data_integrity.hmac_value = decode_base64(parser.attribute("encryptedHmacValue"));
211 [ + + ]: 6 : parser.next_expect(xml::parser::event_type::end_element, xmlns, "dataIntegrity");
212 : :
213 : 6 : auto &key_encryptor = result.key_encryptor;
214 [ + + ]: 12 : parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptors");
215 [ + + ]: 12 : parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptor");
216 [ + + ]: 6 : parser.attribute("uri");
217 : 6 : bool any_password_key = false;
218 : :
219 [ + + + ]: 12 : while (parser.peek() != xml::parser::event_type::end_element)
220 : : {
221 [ + ]: 6 : parser.next_expect(xml::parser::event_type::start_element);
222 : :
223 [ + - + + : 6 : if (parser.namespace_() == xmlns_p && parser.name() == "encryptedKey")
- + - ]
224 : : {
225 : 6 : any_password_key = true;
226 [ + + ]: 12 : key_encryptor.spin_count = parser.attribute<std::size_t>("spinCount");
227 [ + + ]: 12 : key_encryptor.salt_size = parser.attribute<std::size_t>("saltSize");
228 [ + + ]: 12 : key_encryptor.block_size = parser.attribute<std::size_t>("blockSize");
229 [ + + ]: 12 : key_encryptor.key_bits = parser.attribute<std::size_t>("keyBits");
230 [ + + ]: 12 : key_encryptor.hash_size = parser.attribute<std::size_t>("hashSize");
231 [ + + + ]: 12 : key_encryptor.cipher_algorithm = parser.attribute("cipherAlgorithm");
232 [ + + + ]: 12 : key_encryptor.cipher_chaining = parser.attribute("cipherChaining");
233 [ + + ]: 12 : key_encryptor.hash = parser.attribute<xlnt::detail::hash_algorithm>("hashAlgorithm");
234 [ + + + ]: 12 : key_encryptor.salt_value = decode_base64(parser.attribute("saltValue"));
235 [ + + + ]: 12 : key_encryptor.verifier_hash_input = decode_base64(parser.attribute("encryptedVerifierHashInput"));
236 [ + + + ]: 12 : key_encryptor.verifier_hash_value = decode_base64(parser.attribute("encryptedVerifierHashValue"));
237 [ + + + ]: 12 : key_encryptor.encrypted_key_value = decode_base64(parser.attribute("encryptedKeyValue"));
238 : : }
239 : : else
240 : : {
241 [ # # ]:UBC 0 : throw xlnt::unsupported("other encryption key types not supported");
242 : : }
243 : :
244 [ + ]:CBC 6 : parser.next_expect(xml::parser::event_type::end_element);
245 : : }
246 : :
247 [ - + ]: 6 : if (!any_password_key)
248 : : {
249 [ # # ]:UBC 0 : throw xlnt::exception("no password key in keyEncryptors");
250 : : }
251 : :
252 [ + + ]:CBC 12 : parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyEncryptor");
253 [ + + ]: 12 : parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyEncryptors");
254 : :
255 [ + + ]: 6 : parser.next_expect(xml::parser::event_type::end_element, xmlns, "encryption");
256 : :
257 : 6 : return result;
258 : 6 : }
259 : :
260 : 21 : encryption_info read_encryption_info(std::istream &info_stream, const std::u16string &password)
261 : : {
262 : 21 : encryption_info info;
263 : :
264 [ + ]: 21 : info.password = password;
265 : :
266 [ + ]: 21 : auto version_major = read<std::uint16_t>(info_stream);
267 [ + ]: 21 : auto version_minor = read<std::uint16_t>(info_stream);
268 [ + ]: 21 : auto encryption_flags = read<std::uint32_t>(info_stream);
269 : :
270 [ + + + + ]: 21 : info.is_agile = version_major == 4 && version_minor == 4;
271 : :
272 [ + + ]: 21 : if (info.is_agile)
273 : : {
274 [ - + ]: 6 : if (encryption_flags != 0x40)
275 : : {
276 [ # # ]:UBC 0 : throw xlnt::exception("bad header");
277 : : }
278 : :
279 [ + ]:CBC 6 : info.agile = read_agile_encryption_info(info_stream);
280 : : }
281 : : else
282 : : {
283 [ + - + - : 15 : if (version_minor != 2 || (version_major != 2 && version_major != 3 && version_major != 4))
+ + - + ]
284 : : {
285 [ # # ]:UBC 0 : throw xlnt::exception("unsupported encryption version");
286 : : }
287 : :
288 [ - + ]:CBC 15 : if ((encryption_flags & 0x03) != 0) // Reserved1 and Reserved2, MUST be 0
289 : : {
290 [ # # ]:UBC 0 : throw xlnt::exception("bad header");
291 : : }
292 : :
293 [ + - ]:CBC 15 : if ((encryption_flags & 0x04) == 0 // fCryptoAPI
294 [ - + ]: 15 : || (encryption_flags & 0x10) != 0) // fExternal
295 : : {
296 [ # # ]:UBC 0 : throw xlnt::exception("extensible encryption is not supported");
297 : : }
298 : :
299 [ - + ]:CBC 15 : if ((encryption_flags & 0x20) == 0) // fAES
300 : : {
301 [ # # ]:UBC 0 : throw xlnt::exception("not an OOXML document");
302 : : }
303 : :
304 [ + ]:CBC 15 : info.standard = read_standard_encryption_info(info_stream);
305 : : }
306 : :
307 : 21 : return info;
308 :UBC 0 : }
309 : :
310 :CBC 21 : std::vector<std::uint8_t> decrypt_xlsx(
311 : : const std::vector<std::uint8_t> &bytes,
312 : : const std::u16string &password)
313 : : {
314 [ - + ]: 21 : if (bytes.empty())
315 : : {
316 [ # # ]:UBC 0 : throw xlnt::exception("empty file");
317 : : }
318 : :
319 [ + ]:CBC 21 : xlnt::detail::vector_istreambuf buffer(bytes);
320 [ + ]: 21 : std::istream stream(&buffer);
321 [ + ]: 21 : xlnt::detail::compound_document document(stream);
322 : :
323 [ + + ]: 21 : auto &encryption_info_stream = document.open_read_stream("/EncryptionInfo");
324 [ + ]: 21 : auto encryption_info = read_encryption_info(encryption_info_stream, password);
325 : :
326 [ + + ]: 21 : auto &encrypted_package_stream = document.open_read_stream("/EncryptedPackage");
327 : :
328 : 21 : return encryption_info.is_agile
329 : 21 : ? decrypt_xlsx_agile(encryption_info, encrypted_package_stream)
330 [ + + + + : 58 : : decrypt_xlsx_standard(encryption_info, encrypted_package_stream);
+ + + +
+ ]
331 : 36 : }
332 : :
333 : : } // namespace
334 : :
335 : : namespace xlnt {
336 : : namespace detail {
337 : :
338 : 11 : std::vector<std::uint8_t> decrypt_xlsx(const std::vector<std::uint8_t> &data, const std::string &password)
339 : : {
340 [ + + ]: 11 : return ::decrypt_xlsx(data, utf8_to_utf16(password));
341 : : }
342 : :
343 : : #if XLNT_HAS_FEATURE(U8_STRING_VIEW)
344 : 10 : std::vector<std::uint8_t> decrypt_xlsx(const std::vector<std::uint8_t> &data, std::u8string_view password)
345 : : {
346 [ + + ]: 10 : return ::decrypt_xlsx(data, utf8_to_utf16(password));
347 : : }
348 : : #endif
349 : :
350 : : template <typename T>
351 : 21 : void xlsx_consumer::read_internal(std::istream &source, const T &password)
352 : : {
353 [ + ]: 21 : std::vector<std::uint8_t> data((std::istreambuf_iterator<char>(source)), (std::istreambuf_iterator<char>()));
354 [ + ]: 21 : const auto decrypted = decrypt_xlsx(data, password);
355 [ + ]: 16 : vector_istreambuf decrypted_buffer(decrypted);
356 [ + ]: 16 : std::istream decrypted_stream(&decrypted_buffer);
357 [ + ]: 16 : read(decrypted_stream);
358 : 21 : }
359 : :
360 : 11 : void xlsx_consumer::read(std::istream &source, const std::string &password)
361 : : {
362 : 11 : return read_internal(source, password);
363 : : }
364 : :
365 : : #if XLNT_HAS_FEATURE(U8_STRING_VIEW)
366 : 10 : void xlsx_consumer::read(std::istream &source, std::u8string_view password)
367 : : {
368 : 10 : return read_internal(source, password);
369 : : }
370 : : #endif
371 : :
372 : : } // namespace detail
373 : : } // namespace xlnt
|