differential code coverage report with master
Current view: top level - source/detail/cryptography - xlsx_crypto_consumer.cpp (source / functions) Coverage Total Hit UBC CBC
Current: coverage.info Lines: 92.2 % 179 165 14 165
Current Date: 2025-12-15 23:01:28 Functions: 100.0 % 12 12 12
Baseline: coverage_master.info Branches: 76.8 % 259 199 120 398
Baseline Date: 2025-12-15 23:01:27

             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
        

Generated by: LCOV version 2.3.1-beta