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 <xlnt/utils/exceptions.hpp>
26 : : #include <detail/constants.hpp>
27 : : #include <detail/cryptography/aes.hpp>
28 : : #include <detail/cryptography/base64.hpp>
29 : : #include <detail/cryptography/compound_document.hpp>
30 : : #include <detail/cryptography/encryption_info.hpp>
31 : : #include <detail/cryptography/value_traits.hpp>
32 : : #include <detail/cryptography/xlsx_crypto_producer.hpp>
33 : : #include <detail/external/include_libstudxml.hpp>
34 : : #include <detail/serialization/vector_streambuf.hpp>
35 : : #include <detail/serialization/xlsx_producer.hpp>
36 : : #include <detail/serialization/zstream.hpp>
37 : : #include <detail/unicode.hpp>
38 : : #include <detail/utils/string_helpers.hpp>
39 : :
40 : : namespace {
41 : :
42 : : using xlnt::detail::encryption_info;
43 : :
44 :CBC 4 : encryption_info generate_encryption_info(const std::u16string & /*password*/)
45 : : {
46 : 4 : encryption_info result;
47 : :
48 : 4 : result.is_agile = true;
49 : :
50 : 4 : result.agile = encryption_info::agile_encryption_info();
51 : :
52 : 4 : result.agile.key_data.block_size = 16;
53 [ + ]: 4 : result.agile.key_data.cipher_algorithm = "AES";
54 [ + ]: 4 : result.agile.key_data.cipher_chaining = "ChainingModeCBC";
55 [ + ]: 4 : result.agile.key_data.hash_algorithm = "SHA512";
56 : 4 : result.agile.key_data.hash_size = 64;
57 : 4 : result.agile.key_data.key_bits = 256;
58 : 4 : result.agile.key_data.salt_size = 16;
59 : : result.agile.key_data.salt_value =
60 : : {
61 [ + ]: 8 : {40, 183, 193, 64, 115, 97, 10, 177, 122, 50, 243, 123, 229, 145, 162, 247}};
62 : :
63 : : result.agile.data_integrity.hmac_key =
64 : : {
65 : : {90, 206, 203, 147, 102, 81, 82, 14, 118, 94, 168, 38, 200, 79, 13, 147, 60,
66 : : 123, 167, 220, 17, 165, 124, 188, 206, 74, 98, 33, 156, 63, 220, 152, 180, 201,
67 : : 167, 183, 141, 252, 182, 55, 90, 189, 187, 167, 230, 186, 61, 239, 80, 49, 54,
68 [ + ]: 8 : 208, 52, 133, 232, 187, 117, 136, 213, 48, 133, 15, 7, 126}};
69 : : result.agile.data_integrity.hmac_value =
70 : : {
71 : : {49, 128, 174, 178, 161, 48, 1, 82, 241, 103, 72, 223, 103, 111, 204, 73,
72 : : 210, 70, 254, 43, 12, 134, 180, 201, 124, 153, 214, 115, 82, 184, 78, 2,
73 : : 166, 106, 69, 18, 173, 177, 40, 238, 243, 240, 3, 86, 145, 218, 223, 177,
74 [ + ]: 8 : 36, 34, 44, 159, 104, 163, 217, 42, 203, 135, 173, 14, 218, 172, 72, 224}};
75 : :
76 : 4 : result.agile.key_encryptor.spin_count = 100000;
77 : 4 : result.agile.key_encryptor.block_size = 16;
78 [ + ]: 4 : result.agile.key_encryptor.cipher_algorithm = "AES";
79 [ + ]: 4 : result.agile.key_encryptor.cipher_chaining = "ChainingModeCBC";
80 : 4 : result.agile.key_encryptor.hash = xlnt::detail::hash_algorithm::sha512;
81 : 4 : result.agile.key_encryptor.hash_size = 64;
82 : 4 : result.agile.key_encryptor.key_bits = 256;
83 : 4 : result.agile.key_encryptor.salt_size = 16;
84 : : result.agile.key_encryptor.salt_value =
85 : : {
86 [ + ]: 8 : {98, 169, 85, 224, 173, 253, 2, 52, 199, 108, 195, 73, 116, 112, 72, 165}};
87 : : result.agile.key_encryptor.verifier_hash_input =
88 : : {
89 [ + ]: 8 : {179, 105, 118, 193, 217, 180, 248, 7, 174, 45, 186, 17, 202, 101, 178, 12}};
90 : : result.agile.key_encryptor.verifier_hash_value =
91 : : {
92 : : {82, 190, 235, 102, 30, 33, 103, 191, 3, 160, 153, 30, 127, 117, 8, 195, 65,
93 : : 245, 77, 219, 85, 28, 206, 236, 55, 86, 243, 49, 104, 128, 243, 138, 227, 113,
94 : : 82, 88, 88, 73, 243, 108, 193, 11, 84, 162, 235, 189, 9, 137, 151, 97, 43,
95 [ + ]: 8 : 137, 197, 72, 164, 192, 65, 252, 253, 227, 236, 242, 252, 179}};
96 : : result.agile.key_encryptor.encrypted_key_value =
97 : : {
98 : : {220, 6, 106, 218, 31, 210, 9, 75, 28, 154, 173, 232, 190, 109, 112, 203, 25,
99 [ + ]: 8 : 5, 45, 152, 75, 131, 122, 17, 166, 95, 117, 124, 121, 123, 32, 133}};
100 : :
101 : 4 : return result;
102 :UBC 0 : }
103 : :
104 :CBC 4 : void write_agile_encryption_info(
105 : : const encryption_info &info,
106 : : std::ostream &info_stream)
107 : : {
108 : 4 : const auto version_major = std::uint16_t(4);
109 : 4 : const auto version_minor = std::uint16_t(4);
110 : 4 : const auto encryption_flags = std::uint32_t(0x40);
111 : :
112 [ + ]: 4 : info_stream.write(reinterpret_cast<const char *>(&version_major), sizeof(std::uint16_t));
113 [ + ]: 4 : info_stream.write(reinterpret_cast<const char *>(&version_minor), sizeof(std::uint16_t));
114 [ + ]: 4 : info_stream.write(reinterpret_cast<const char *>(&encryption_flags), sizeof(std::uint32_t));
115 : :
116 [ + + + - : 6 : static const auto &xmlns = xlnt::constants::ns("encryption");
+ + - - ]
117 [ + + + - : 6 : static const auto &xmlns_p = xlnt::constants::ns("encryption-password");
+ + - - ]
118 : :
119 [ + + ]: 8 : xml::serializer serializer(info_stream, "EncryptionInfo");
120 : :
121 [ + + ]: 4 : serializer.start_element(xmlns, "encryption");
122 : :
123 [ + ]: 4 : const auto key_data = info.agile.key_data;
124 [ + + ]: 8 : serializer.start_element(xmlns, "keyData");
125 [ + + ]: 8 : serializer.attribute("saltSize", key_data.salt_size);
126 [ + + ]: 8 : serializer.attribute("blockSize", key_data.block_size);
127 [ + + ]: 8 : serializer.attribute("keyBits", key_data.key_bits);
128 [ + + ]: 8 : serializer.attribute("hashSize", key_data.hash_size);
129 [ + + ]: 8 : serializer.attribute("cipherAlgorithm", key_data.cipher_algorithm);
130 [ + + ]: 8 : serializer.attribute("cipherChaining", key_data.cipher_chaining);
131 [ + + ]: 4 : serializer.attribute("hashAlgorithm", key_data.hash_algorithm);
132 [ + + ]: 4 : serializer.attribute("saltValue",
133 [ + ]: 8 : xlnt::detail::encode_base64(key_data.salt_value));
134 [ + + ]: 4 : serializer.end_element(xmlns, "keyData");
135 : :
136 [ + ]: 4 : const auto data_integrity = info.agile.data_integrity;
137 [ + + ]: 4 : serializer.start_element(xmlns, "dataIntegrity");
138 [ + + ]: 4 : serializer.attribute("encryptedHmacKey",
139 [ + ]: 8 : xlnt::detail::encode_base64(data_integrity.hmac_key));
140 [ + + ]: 4 : serializer.attribute("encryptedHmacValue",
141 [ + ]: 8 : xlnt::detail::encode_base64(data_integrity.hmac_value));
142 [ + + ]: 4 : serializer.end_element(xmlns, "dataIntegrity");
143 : :
144 [ + ]: 4 : const auto key_encryptor = info.agile.key_encryptor;
145 [ + + ]: 8 : serializer.start_element(xmlns, "keyEncryptors");
146 [ + + ]: 8 : serializer.start_element(xmlns, "keyEncryptor");
147 [ + + ]: 8 : serializer.attribute("uri", "");
148 [ + + ]: 8 : serializer.start_element(xmlns_p, "encryptedKey");
149 [ + + ]: 8 : serializer.attribute("spinCount", key_encryptor.spin_count);
150 [ + + ]: 8 : serializer.attribute("saltSize", key_encryptor.salt_size);
151 [ + + ]: 8 : serializer.attribute("blockSize", key_encryptor.block_size);
152 [ + + ]: 8 : serializer.attribute("keyBits", key_encryptor.key_bits);
153 [ + + ]: 8 : serializer.attribute("hashSize", key_encryptor.hash_size);
154 [ + + ]: 8 : serializer.attribute("cipherAlgorithm", key_encryptor.cipher_algorithm);
155 [ + + ]: 8 : serializer.attribute("cipherChaining", key_encryptor.cipher_chaining);
156 [ + + ]: 4 : serializer.attribute("hashAlgorithm", key_encryptor.hash);
157 [ + + ]: 4 : serializer.attribute("saltValue",
158 [ + ]: 8 : xlnt::detail::encode_base64(key_encryptor.salt_value));
159 [ + + ]: 4 : serializer.attribute("encryptedVerifierHashInput",
160 [ + ]: 8 : xlnt::detail::encode_base64(key_encryptor.verifier_hash_input));
161 [ + + ]: 4 : serializer.attribute("encryptedVerifierHashValue",
162 [ + ]: 8 : xlnt::detail::encode_base64(key_encryptor.verifier_hash_value));
163 [ + + ]: 4 : serializer.attribute("encryptedKeyValue",
164 [ + ]: 8 : xlnt::detail::encode_base64(key_encryptor.encrypted_key_value));
165 [ + + ]: 8 : serializer.end_element(xmlns_p, "encryptedKey");
166 [ + + ]: 8 : serializer.end_element(xmlns, "keyEncryptor");
167 [ + + ]: 8 : serializer.end_element(xmlns, "keyEncryptors");
168 : :
169 [ + + ]: 4 : serializer.end_element(xmlns, "encryption");
170 : 4 : }
171 : :
172 :UBC 0 : void write_standard_encryption_info(const encryption_info &info, std::ostream &info_stream)
173 : : {
174 : 0 : auto result = std::vector<std::uint8_t>();
175 : 0 : auto writer = xlnt::detail::binary_writer<std::uint8_t>(result);
176 : :
177 : 0 : const auto version_major = std::uint16_t(4);
178 : 0 : const auto version_minor = std::uint16_t(2);
179 : 0 : const auto encryption_flags = std::uint32_t(0x10 & 0x20);
180 : :
181 [ # ]: 0 : writer.write(version_major);
182 [ # ]: 0 : writer.write(version_minor);
183 [ # ]: 0 : writer.write(encryption_flags);
184 : :
185 : 0 : const auto header_length = std::uint32_t(32); // calculate this!
186 : :
187 [ # ]: 0 : writer.write(header_length);
188 [ # ]: 0 : writer.write(std::uint32_t(0)); // skip_flags
189 [ # ]: 0 : writer.write(std::uint32_t(0)); // size_extra
190 [ # ]: 0 : writer.write(std::uint32_t(0x0000660E));
191 [ # ]: 0 : writer.write(std::uint32_t(0x00008004));
192 [ # ]: 0 : writer.write(std::uint32_t(info.standard.key_bits));
193 [ # ]: 0 : writer.write(std::uint32_t(0x00000018));
194 [ # ]: 0 : writer.write(std::uint32_t(0));
195 [ # ]: 0 : writer.write(std::uint32_t(0));
196 : :
197 [ # ]: 0 : const auto provider = std::u16string(u"Microsoft Enhanced RSA and AES Cryptographic Provider");
198 [ # # ]: 0 : writer.append(xlnt::detail::string_to_bytes(provider));
199 : :
200 [ # ]: 0 : writer.write(std::uint32_t(info.standard.salt.size()));
201 [ # ]: 0 : writer.append(info.standard.salt);
202 : :
203 [ # ]: 0 : writer.append(info.standard.encrypted_verifier);
204 : :
205 [ # ]: 0 : writer.write(std::uint32_t(20));
206 [ # ]: 0 : writer.append(info.standard.encrypted_verifier_hash);
207 : :
208 [ # ]: 0 : info_stream.write(reinterpret_cast<char *>(result.data()),
209 : 0 : static_cast<std::streamsize>(result.size()));
210 : 0 : }
211 : :
212 :CBC 4 : void encrypt_xlsx_agile(
213 : : const encryption_info &info,
214 : : const std::vector<std::uint8_t> &plaintext,
215 : : std::ostream &ciphertext_stream)
216 : : {
217 : 4 : const auto length = static_cast<std::uint64_t>(plaintext.size());
218 [ + ]: 4 : ciphertext_stream.write(reinterpret_cast<const char *>(&length), sizeof(std::uint64_t));
219 : :
220 [ + ]: 4 : auto key = info.calculate_key();
221 : :
222 : 4 : auto salt_size = info.agile.key_data.salt_size;
223 [ + ]: 4 : auto salt_with_block_key = info.agile.key_data.salt_value;
224 [ + ]: 4 : salt_with_block_key.resize(salt_size + sizeof(std::uint32_t), 0);
225 : 4 : auto &segment_index = *reinterpret_cast<std::uint32_t *>(salt_with_block_key.data() + salt_size);
226 : :
227 [ + ]: 4 : auto segment = std::vector<std::uint8_t>(4096, 0);
228 : :
229 [ + + ]: 132 : for (auto i = std::size_t(0); i < length; i += 4096)
230 : : {
231 [ + ]: 128 : auto iv = hash(info.agile.key_encryptor.hash, salt_with_block_key);
232 [ + ]: 128 : iv.resize(16);
233 : :
234 : 128 : auto start = plaintext.begin() + static_cast<std::ptrdiff_t>(i);
235 : 128 : auto bytes = std::min(std::size_t(length - i), std::size_t(4096));
236 [ + ]: 128 : std::copy(start, start + static_cast<std::ptrdiff_t>(bytes), segment.begin());
237 [ + ]: 128 : auto encrypted_segment = xlnt::detail::aes_cbc_encrypt(segment, key, iv);
238 [ + ]: 128 : ciphertext_stream.write(reinterpret_cast<char *>(encrypted_segment.data()),
239 : : static_cast<std::streamsize>(bytes));
240 : :
241 : 128 : ++segment_index;
242 : 128 : }
243 : 4 : }
244 : :
245 :UBC 0 : void encrypt_xlsx_standard(
246 : : const encryption_info &info,
247 : : const std::vector<std::uint8_t> &plaintext,
248 : : std::ostream &ciphertext_stream)
249 : : {
250 : 0 : const auto length = static_cast<std::uint64_t>(plaintext.size());
251 [ # ]: 0 : ciphertext_stream.write(reinterpret_cast<const char *>(&length), sizeof(std::uint64_t));
252 : :
253 [ # ]: 0 : auto key = info.calculate_key();
254 [ # ]: 0 : auto segment = std::vector<std::uint8_t>(4096, 0);
255 : :
256 [ # # ]: 0 : for (auto i = std::size_t(0); i < length; ++i)
257 : : {
258 : 0 : auto start = plaintext.begin() + static_cast<std::ptrdiff_t>(i);
259 : 0 : auto bytes = std::min(std::size_t(length - i), std::size_t(4096));
260 [ # ]: 0 : std::copy(start, start + static_cast<std::ptrdiff_t>(bytes), segment.begin());
261 [ # ]: 0 : auto encrypted_segment = xlnt::detail::aes_ecb_encrypt(segment, key);
262 [ # ]: 0 : ciphertext_stream.write(reinterpret_cast<char *>(encrypted_segment.data()),
263 : : static_cast<std::streamsize>(bytes));
264 : 0 : }
265 : 0 : }
266 : :
267 :CBC 4 : std::vector<std::uint8_t> encrypt_xlsx(
268 : : const std::vector<std::uint8_t> &plaintext,
269 : : const std::u16string &password)
270 : : {
271 [ + ]: 4 : auto encryption_info = generate_encryption_info(password);
272 [ + ]: 4 : encryption_info.password = u"secret";
273 : :
274 : 4 : auto ciphertext = std::vector<std::uint8_t>();
275 : :
276 [ + ]: 4 : xlnt::detail::vector_ostreambuf buffer(ciphertext);
277 [ + ]: 4 : std::ostream stream(&buffer);
278 [ + ]: 4 : xlnt::detail::compound_document document(stream);
279 : :
280 [ + - ]: 4 : if (encryption_info.is_agile)
281 : : {
282 [ + + ]: 4 : write_agile_encryption_info(encryption_info,
283 [ + ]: 12 : document.open_write_stream("/EncryptionInfo"));
284 [ + + ]: 4 : encrypt_xlsx_agile(encryption_info, plaintext,
285 [ + ]: 12 : document.open_write_stream("/EncryptedPackage"));
286 : : }
287 : : else
288 : : {
289 [ # # ]:UBC 0 : write_standard_encryption_info(encryption_info,
290 [ # ]: 0 : document.open_write_stream("/EncryptionInfo"));
291 [ # # ]: 0 : encrypt_xlsx_standard(encryption_info, plaintext,
292 [ # ]: 0 : document.open_write_stream("/EncryptedPackage"));
293 : : }
294 : :
295 :CBC 4 : return ciphertext;
296 : 4 : }
297 : :
298 : : } // namespace
299 : :
300 : : namespace xlnt {
301 : : namespace detail {
302 : :
303 :UBC 0 : std::vector<std::uint8_t> encrypt_xlsx(
304 : : const std::vector<std::uint8_t> &plaintext,
305 : : const std::string &password)
306 : : {
307 [ # # ]: 0 : return ::encrypt_xlsx(plaintext, utf8_to_utf16(password));
308 : : }
309 : :
310 : : #if XLNT_HAS_FEATURE(U8_STRING_VIEW)
311 : 0 : std::vector<std::uint8_t> encrypt_xlsx(
312 : : const std::vector<std::uint8_t> &plaintext,
313 : : std::u8string_view password)
314 : : {
315 [ # # ]: 0 : return ::encrypt_xlsx(plaintext, utf8_to_utf16(password));
316 : : }
317 : : #endif
318 : :
319 : : template <typename T>
320 :CBC 4 : void xlsx_producer::write_internal(std::ostream &destination, const T &password)
321 : : {
322 : 4 : std::vector<std::uint8_t> plaintext;
323 [ + ]: 4 : vector_ostreambuf plaintext_buffer(plaintext);
324 [ + ]: 4 : std::ostream decrypted_stream(&plaintext_buffer);
325 [ + ]: 4 : write(decrypted_stream);
326 : 4 : archive_.reset();
327 : :
328 [ + + ]: 4 : const auto ciphertext = ::encrypt_xlsx(plaintext, utf8_to_utf16(password));
329 [ + ]: 4 : vector_istreambuf encrypted_buffer(ciphertext);
330 : :
331 [ + ]: 4 : destination << &encrypted_buffer;
332 : 4 : }
333 : :
334 : 3 : void xlsx_producer::write(std::ostream &destination, const std::string &password)
335 : : {
336 : 3 : write_internal(destination, password);
337 : 3 : }
338 : :
339 : : #if XLNT_HAS_FEATURE(U8_STRING_VIEW)
340 : 1 : void xlsx_producer::write(std::ostream &destination, std::u8string_view password)
341 : : {
342 : 1 : write_internal(destination, password);
343 : 1 : }
344 : : #endif
345 : :
346 : : } // namespace detail
347 : : } // namespace xlnt
|