diff --git a/src/cli/extract.cpp b/src/cli/extract.cpp index e9a078d..c738288 100644 --- a/src/cli/extract.cpp +++ b/src/cli/extract.cpp @@ -1100,7 +1100,7 @@ void process_file(const fs::path & installer, const extract_options & o) { if(o.test || o.extract) { boost::uint64_t offset = info.data_entries[file.entry().location].uncompressed_size; boost::uint32_t sort_slice = info.data_entries[file.entry().location].chunk.first_slice; - boost::uint32_t sort_offset = info.data_entries[file.entry().location].chunk.sort_offset; + boost::uint64_t sort_offset = info.data_entries[file.entry().location].chunk.sort_offset; BOOST_FOREACH(boost::uint32_t location, file.entry().additional_locations) { setup::data_entry & data = info.data_entries[location]; files_for_location[location].push_back(output_location(&file, offset)); diff --git a/src/loader/offsets.cpp b/src/loader/offsets.cpp index 1efe288..f151470 100644 --- a/src/loader/offsets.cpp +++ b/src/loader/offsets.cpp @@ -219,8 +219,8 @@ bool offsets::load_offsets_at(std::istream & is, boost::uint32_t pos) { boost::uint32_t actual = checksum.finalize(); if(actual != expected) { log_warning << "Setup loader checksum mismatch! Expected: " - << print_hex(expected) - << " Actual: " << print_hex(actual); + << print_hex(expected) + << " Actual: " << print_hex(actual); } } diff --git a/src/setup/data.cpp b/src/setup/data.cpp index 5af88f7..c22b72b 100644 --- a/src/setup/data.cpp +++ b/src/setup/data.cpp @@ -57,7 +57,7 @@ void data_entry::load(std::istream & is, const info & i) { } if(i.version >= INNO_VERSION(6, 5, 2)) { - chunk.sort_offset = chunk.offset = util::load(is); + chunk.sort_offset = chunk.offset = util::load(is); } else { chunk.sort_offset = chunk.offset = util::load(is); } diff --git a/src/setup/file.cpp b/src/setup/file.cpp index e1ed946..112f8a7 100644 --- a/src/setup/file.cpp +++ b/src/setup/file.cpp @@ -29,6 +29,11 @@ namespace setup { namespace { +STORED_ENUM_MAP(stored_file_verification_type, file_entry::fvNone, + file_entry::fvNone, + file_entry::fvHash, + file_entry::fvISSig, +); enum file_copy_mode { cmNormal, @@ -60,6 +65,14 @@ STORED_ENUM_MAP(stored_file_type_1, file_entry::UserFile, } // namespace setup +NAMED_ENUM(setup::file_entry::file_verification_type) + +NAMES(setup::file_entry::file_verification_type, "File Verification Type", + "none", + "hash", + "IS sig", +) + NAMED_ENUM(setup::file_copy_mode) NAMES(setup::file_copy_mode, "File Copy Mode", @@ -92,26 +105,25 @@ void file_entry::load(std::istream & is, const info & i) { load_condition_data(is, i); - if(i.version >= INNO_VERSION(6, 5, 0)) { - is >> util::encoded_string(excludes, i.codepage, i.header.lead_bytes); - is >> util::encoded_string(download_issig_source, i.codepage, i.header.lead_bytes); - is >> util::encoded_string(download_user_name, i.codepage, i.header.lead_bytes); - is >> util::encoded_string(download_password, i.codepage, i.header.lead_bytes); - is >> util::encoded_string(extract_archive_password, i.codepage, i.header.lead_bytes); - - // Verification structure - std::string issig_allowed_keys; - is >> util::ansi_string(issig_allowed_keys); - char hash[32]; - is.read(hash, 32); - boost::uint8_t verification_type = util::load(is); - } else { - excludes.clear(); - download_issig_source.clear(); - download_user_name.clear(); - download_password.clear(); - extract_archive_password.clear(); - } + if(i.version >= INNO_VERSION(6, 5, 0)) { + is >> util::encoded_string(excludes, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(download_issig_source, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(download_user_name, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(download_password, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(extract_archive_password, i.codepage, i.header.lead_bytes); + + // Verification structure + is >> util::ansi_string(issig_allowed_keys); + is.read(checksum.sha256, std::streamsize(sizeof(checksum.sha256))); + checksum.type = crypto::SHA256; + verification = stored_enum(is).get(); + } else { + excludes.clear(); + download_issig_source.clear(); + download_user_name.clear(); + download_password.clear(); + extract_archive_password.clear(); + } load_version_data(is, i.version); diff --git a/src/setup/file.hpp b/src/setup/file.hpp index 66aeb5c..7241e71 100644 --- a/src/setup/file.hpp +++ b/src/setup/file.hpp @@ -84,6 +84,12 @@ struct file_entry : public item { IsReadmeFile ); + enum file_verification_type { + fvNone, + fvHash, + fvISSig, + }; + enum file_type { UserFile, UninstExe, @@ -98,11 +104,14 @@ struct file_entry : public item { std::string destination; std::string install_font_name; std::string strong_assembly_name; - std::string excludes; - std::string download_issig_source; - std::string download_user_name; - std::string download_password; - std::string extract_archive_password; + std::string excludes; + std::string download_issig_source; + std::string download_user_name; + std::string download_password; + std::string extract_archive_password; + + std::string issig_allowed_keys; + file_verification_type verification; boost::uint32_t location; //!< index into the data entry list boost::uint32_t attributes; diff --git a/src/setup/header.cpp b/src/setup/header.cpp index 8feb35b..a954734 100644 --- a/src/setup/header.cpp +++ b/src/setup/header.cpp @@ -710,7 +710,7 @@ header::flags header::load_flags(std::istream & is, const version & version) { flagreader.add(AppendDefaultDirName); flagreader.add(AppendDefaultGroupName); } - if(version >= INNO_VERSION(4, 2, 2)) { + if(version >= INNO_VERSION(4, 2, 2) && version < INNO_VERSION(6, 5, 0)) { flagreader.add(EncryptionUsed); } if(version >= INNO_VERSION(5, 0, 4) && version < INNO_VERSION(5, 6, 1)) { @@ -830,7 +830,6 @@ NAMES(setup::header::flags, "Setup Option", "wizard image stretch", "append default dir name", "append default group name", - "encrypted", "changes environment", "show undisplayable languages", "setup logging", @@ -858,6 +857,7 @@ NAMES(setup::header::flags, "Setup Option", "disable dir exists warning", "back solid", "overwrite uninst reg entries", + "encrypted", ) NAMES(setup::header::architecture_types, "Architecture", diff --git a/src/setup/header.hpp b/src/setup/header.hpp index a944d5e..7c748a5 100644 --- a/src/setup/header.hpp +++ b/src/setup/header.hpp @@ -87,7 +87,6 @@ struct header { WizardImageStretch, AppendDefaultDirName, AppendDefaultGroupName, - EncryptionUsed, ChangesEnvironment, ShowUndisplayableLanguages, SetupLogging, @@ -116,7 +115,8 @@ struct header { DetectLanguageUsingLocale, DisableDirExistsWarning, BackSolid, - OverwriteUninstRegEntries + OverwriteUninstRegEntries, + EncryptionUsed ); diff --git a/src/setup/info.cpp b/src/setup/info.cpp index d605975..c129693 100644 --- a/src/setup/info.cpp +++ b/src/setup/info.cpp @@ -39,6 +39,7 @@ #include "setup/icon.hpp" #include "setup/ini.hpp" #include "setup/item.hpp" +#include "setup/issigkey.hpp" #include "setup/language.hpp" #include "setup/message.hpp" #include "setup/permission.hpp" @@ -159,7 +160,7 @@ void info::try_load(std::istream & is, entry_types entries, util::codepage_id fo debug("loading main header"); header.load(*reader, version); - debug("loading languages"); + debug("loading " << header.language_count << " languages"); load_entries(*reader, entries, header.language_count, languages, Languages); debug("determining encoding"); @@ -193,39 +194,36 @@ void info::try_load(std::istream & is, entry_types entries, util::codepage_id fo load_wizard_and_decompressor(*reader, version, header, *this, entries); } - debug("loading messages"); + debug("loading " << header.message_count << " messages"); load_entries(*reader, entries, header.message_count, messages, Messages); - debug("loading permissions"); + debug("loading " << header.permission_count << " permissions"); load_entries(*reader, entries, header.permission_count, permissions, Permissions); - debug("loading types"); + debug("loading " << header.type_count << " types"); load_entries(*reader, entries, header.type_count, types, Types); - debug("loading components"); + debug("loading " << header.component_count << " components"); load_entries(*reader, entries, header.component_count, components, Components); - debug("loading tasks"); + debug("loading " << header.task_count << " tasks"); load_entries(*reader, entries, header.task_count, tasks, Tasks); - debug("loading directories"); + debug("loading " << header.directory_count << " directories"); load_entries(*reader, entries, header.directory_count, directories, Directories); - debug("slipping issig keys"); - for(size_t i = 0; i < header.issig_key_count; i++) { - issig_key_entry entry; - entry.load(*reader, *this); - } - debug("loading files"); + debug("loading " << header.issig_key_count << " issigs"); + load_entries(*reader, entries, header.issig_key_count, issig_keys, ISSigs); + debug("loading " << header.file_count << " files"); load_entries(*reader, entries, header.file_count, files, Files); - debug("loading icons"); + debug("loading " << header.icon_count << " icons"); load_entries(*reader, entries, header.icon_count, icons, Icons); - debug("loading ini entries"); + debug("loading " << header.ini_entry_count << " ini entries"); load_entries(*reader, entries, header.ini_entry_count, ini_entries, IniEntries); - debug("loading registry entries"); + debug("loading " << header.registry_entry_count << " registry entries"); load_entries(*reader, entries, header.registry_entry_count, registry_entries, RegistryEntries); - debug("loading delete entries"); + debug("loading " << header.delete_entry_count << " delete entries"); load_entries(*reader, entries, header.delete_entry_count, delete_entries, DeleteEntries); - debug("loading uninstall delete entries"); + debug("loading " << header.uninstall_delete_entry_count << " uninstall delete entries"); load_entries(*reader, entries, header.uninstall_delete_entry_count, uninstall_delete_entries, UninstallDeleteEntries); - debug("loading run entries"); + debug("loading " << header.run_entry_count << " run entries"); load_entries(*reader, entries, header.run_entry_count, run_entries, RunEntries); - debug("loading uninstall run entries"); + debug("loading " << header.uninstall_run_entry_count << " uninstall run entries"); load_entries(*reader, entries, header.uninstall_run_entry_count, uninstall_run_entries, UninstallRunEntries); @@ -245,49 +243,13 @@ void info::try_load(std::istream & is, entry_types entries, util::codepage_id fo } void info::load(std::istream & is, entry_types entries, util::codepage_id force_codepage, - boost::uint32_t loader_revision) { + boost::uint32_t loader_revision) { version.load(is); if(loader_revision == 2 && version >= INNO_VERSION(6, 5, 0)) { version.set_64bit(); } - if(version >= INNO_VERSION(6, 5, 0)) { - boost::uint32_t eh_expected_crc = util::load(is); - - encryption_header eh; - if(is.read(reinterpret_cast(&eh), sizeof(eh)).fail()) { - throw std::runtime_error("failed to read encryption header"); - } - - crypto::crc32 eh_crc; - eh_crc.init(); - eh_crc.update(reinterpret_cast(&eh), sizeof(eh)); - if(eh_crc.finalize() != eh_expected_crc) { - //throw std::runtime_error("encryption header CRC mismatch"); - } - - if(eh.encryption_use == 2) { - throw std::runtime_error("full encryption mode is not supported"); - } - - if(eh.encryption_use == 1) { - header.password.type = crypto::PBKDF2_SHA256_XChaCha20; - header.password_salt.resize(44); - std::memcpy(&header.password_salt[0], eh.kdf_salt, 16); - util::little_endian::store( - boost::uint32_t(eh.kdf_iterations), &header.password_salt[16] - ); - std::memcpy(&header.password_salt[20], &eh.base_nonce, sizeof(eh.base_nonce)); - - std::memcpy(header.password.sha256, &eh.password_test, 4); - } else { - header.password.type = crypto::None; - header.password_salt.clear(); - std::memset(header.password.sha256, 0, 4); - } - } - if(!version.known) { if(entries & NoUnknownVersion) { std::ostringstream oss; @@ -309,6 +271,31 @@ void info::load(std::istream & is, entry_types entries, util::codepage_id force_ entries |= NoSkip; } + if(version >= INNO_VERSION(6, 5, 0)) { + boost::uint32_t expected_encryption_crc = util::load(is); + crypto::crc32 checksum; + checksum.init(); + boost::uint8_t encryption_use = checksum.load(is); + if(encryption_use != 0) { + log_error << "Unsupported encrypted setup"; + } + + for(int i = 0; i < 16; i++) { + /* KDFSalt */(void)checksum.load(is); + } + /* KDFIterations */(void)checksum.load(is); + /* BaseNonce.RandomXorStartOffset */(void)checksum.load(is); + /* BaseNonce.RandomXorFirstSlice */(void)checksum.load(is); + for(int i = 0; i < 3; i++) { + /* BaseNonce.RemainingRandom */(void)checksum.load(is); + } + /* PasswordTest */(void)checksum.load(is); + + if(checksum.finalize() != expected_encryption_crc) { + log_warning << "Encryption header checksum mismatch!"; + } + } + bool parsed_without_errors = false; std::streampos start = is.tellg(); for(;;) { diff --git a/src/setup/info.hpp b/src/setup/info.hpp index bfd5fb1..d4e1fb5 100644 --- a/src/setup/info.hpp +++ b/src/setup/info.hpp @@ -41,6 +41,7 @@ struct component_entry; struct data_entry; struct delete_entry; struct directory_entry; +struct issig_key_entry; struct file_entry; struct icon_entry; struct ini_entry; @@ -52,20 +53,6 @@ struct run_entry; struct task_entry; struct type_entry; -#pragma pack(push, 1) -struct encryption_header { - boost::uint8_t encryption_use; // 0=none, 1=files, 2=full - boost::uint8_t kdf_salt[16]; - boost::int32_t kdf_iterations; - struct { - boost::int64_t random_xor_start_offset; - boost::int32_t random_xor_first_slice; - boost::int32_t remaining_random[3]; - } base_nonce; - boost::int32_t password_test; -}; -#pragma pack(pop) - /*! * Class used to hold and load the various \ref setup headers. */ @@ -81,6 +68,7 @@ struct info { DeleteEntries, UninstallDeleteEntries, Directories, + ISSigs, Files, Icons, IniEntries, @@ -110,6 +98,7 @@ struct info { std::vector delete_entries; //! \c DeleteEntries std::vector uninstall_delete_entries; //! \c UninstallDeleteEntries std::vector directories; //! \c Directories + std::vector issig_keys; //! \c ISSigKeys std::vector files; //! \c Files std::vector icons; //! \c Icons std::vector ini_entries; //! \c IniEntries @@ -121,7 +110,6 @@ struct info { std::vector uninstall_run_entries; //! \c UninstallRunEntries std::vector tasks; //! \c Tasks std::vector types; //! \c Types - std::vector issig_keys; //! \c ISSigKeys //! Images displayed in the installer UI. //! Loading enabled by \c WizardImages @@ -147,7 +135,7 @@ struct info { * \param force_codepage Windows codepage to use for strings in ANSI installers. */ void load(std::istream & is, entry_types entries, util::codepage_id force_codepage = 0, - boost::uint32_t loader_revision = 1); + boost::uint32_t loader_revision = 1); std::string get_key(const std::string & password); diff --git a/src/setup/issigkey.cpp b/src/setup/issigkey.cpp index f61e9be..23475e5 100644 --- a/src/setup/issigkey.cpp +++ b/src/setup/issigkey.cpp @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2011-2025 Daniel Scharrer + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the author(s) be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + #include "setup/issigkey.hpp" #include "setup/info.hpp" #include "util/load.hpp" @@ -10,4 +30,4 @@ void issig_key_entry::load(std::istream & is, const info & i) { is >> util::encoded_string(runtime_id, i.codepage); } -} // namespace setup \ No newline at end of file +} // namespace setup diff --git a/src/setup/issigkey.hpp b/src/setup/issigkey.hpp index a119375..15a346d 100644 --- a/src/setup/issigkey.hpp +++ b/src/setup/issigkey.hpp @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2011-2025 Daniel Scharrer + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the author(s) be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + #ifndef INNOEXTRACT_SETUP_ISSIGKEY_HPP #define INNOEXTRACT_SETUP_ISSIGKEY_HPP @@ -18,4 +38,4 @@ struct issig_key_entry { } // namespace setup -#endif // INNOEXTRACT_SETUP_ISSIGKEY_HPP \ No newline at end of file +#endif // INNOEXTRACT_SETUP_ISSIGKEY_HPP diff --git a/src/setup/version.hpp b/src/setup/version.hpp index d2f0dd4..f040c9d 100644 --- a/src/setup/version.hpp +++ b/src/setup/version.hpp @@ -78,9 +78,9 @@ struct version { void load(std::istream & is); - boost::uint16_t bits() const { return (variant & Bits16) ? 16 : 32; } + boost::uint16_t bits() const { return (variant & Bits16) ? 16 : 32; } - void set_64bit() { variant |= Bits64; } + void set_64bit() { variant |= Bits64; } bool is_unicode() const { return (variant & Unicode) != 0; } bool is_isx() const { return (variant & ISX) != 0; } bool is_64bit() const { return (variant & Bits64) != 0; } diff --git a/src/stream/block.cpp b/src/stream/block.cpp index 0f3c04f..c4e627c 100644 --- a/src/stream/block.cpp +++ b/src/stream/block.cpp @@ -179,11 +179,11 @@ block_reader::pointer block_reader::get(std::istream & base, const setup::versio stored_size = actual_checksum.load(base); boost::uint8_t compressed = actual_checksum.load(base); -#ifdef DEBUG + #ifdef DEBUG debug("" << (version.is_64bit() ? "64 bit offsets" : "32 bit offsets")); debug("read stored_size: " << print_hex(stored_size) << " (" << stored_size << " bytes)"); debug("compressed flag: " << print_hex(boost::uint32_t(compressed))); -#endif + #endif compression = compressed ? (version >= INNO_VERSION(4, 1, 6) ? LZMA1 : Zlib) : Stored; } else { diff --git a/src/stream/chunk.hpp b/src/stream/chunk.hpp index fae3484..16c3ccb 100644 --- a/src/stream/chunk.hpp +++ b/src/stream/chunk.hpp @@ -81,9 +81,9 @@ struct chunk { boost::uint32_t first_slice; //!< Slice where the chunk starts. boost::uint32_t last_slice; //!< Slice where the chunk ends. - boost::uint32_t sort_offset; + boost::uint64_t sort_offset; - boost::uint32_t offset; //!< Offset of the compressed chunk in firstSlice. + boost::uint64_t offset; //!< Offset of the compressed chunk in firstSlice. boost::uint64_t size; //! Total compressed size of the chunk. compression_method compression; //!< Compression method used by the chunk. diff --git a/src/stream/slice.cpp b/src/stream/slice.cpp index 6a5923a..27491f0 100644 --- a/src/stream/slice.cpp +++ b/src/stream/slice.cpp @@ -147,7 +147,7 @@ bool slice_reader::open_file(const path_type & file) { slice_size = boost::uint32_t(slice_size_64); - if(std::streampos(slice_size_64) > file_size) { + if(slice_size_64 > boost::uint64_t(file_size)) { ifs.close(); std::ostringstream oss; oss << "bad slice size in " << file << ": " << slice_size_64 << " > " << file_size; @@ -247,7 +247,7 @@ void slice_reader::open(size_t slice) { throw slice_error(oss.str()); } -bool slice_reader::seek(size_t slice, boost::uint32_t offset) { +bool slice_reader::seek(size_t slice, boost::uint64_t offset) { seek(slice); @@ -257,7 +257,7 @@ bool slice_reader::seek(size_t slice, boost::uint32_t offset) { return false; } - if(is->seekg(offset).fail()) { + if(is->seekg(std::streamoff(offset)).fail()) { return false; } diff --git a/src/stream/slice.hpp b/src/stream/slice.hpp index b2ec52c..9c25247 100644 --- a/src/stream/slice.hpp +++ b/src/stream/slice.hpp @@ -126,7 +126,7 @@ public: * \return \c false if the requested slice could not be opened, or if the requested * offset is not a valid position in that slice - \c true otherwise. */ - bool seek(size_t slice, boost::uint32_t offset); + bool seek(size_t slice, boost::uint64_t offset); /*! * Read a number of bytes starting at the current slice and offset within that slice. diff --git a/src/util/output.hpp b/src/util/output.hpp index 69d8b41..350f431 100644 --- a/src/util/output.hpp +++ b/src/util/output.hpp @@ -73,7 +73,7 @@ inline std::ostream & operator<<(std::ostream & os, const if_not_empty & s) { if(s.value.length() > 100) { color::shell_command prev = color::current; return os << s.name << ": " << color::white << s.value.length() << prev - << " bytes" << '\n'; + << " bytes" << '\n'; } else if(!s.value.empty()) { return os << s.name << ": " << quoted(s.value) << '\n'; } else { @@ -191,8 +191,7 @@ struct print_hex_dump { size_t start_offset; size_t bytes_per_line; - explicit print_hex_dump(const char * buffer, size_t length, - size_t offset = 0, size_t width = 16) + explicit print_hex_dump(const char * buffer, size_t length, size_t offset = 0, size_t width = 16) : data(buffer), size(length), start_offset(offset), bytes_per_line(width) { } }; @@ -236,13 +235,11 @@ inline std::ostream & operator<<(std::ostream & os, const print_hex_dump & s) { } // namespace detail -inline detail::print_hex_dump print_hex_dump( - const char * data, size_t size, size_t offset = 0, size_t width = 16) { +inline detail::print_hex_dump print_hex_dump(const char * data, size_t size, size_t offset = 0, size_t width = 16) { return detail::print_hex_dump(data, size, offset, width); } -inline detail::print_hex_dump print_hex_dump( - const std::string & data, size_t offset = 0, size_t width = 16) { +inline detail::print_hex_dump print_hex_dump(const std::string & data, size_t offset = 0, size_t width = 16) { return print_hex_dump(data.c_str(), data.size(), offset, width); }