From 3dca6d35c38fbfcc5b00cd17cc6c13c3466bcb7e Mon Sep 17 00:00:00 2001 From: kimci86 Date: Tue, 6 Feb 2024 22:53:07 +0100 Subject: [PATCH 1/4] Add clang-format configuration file --- .clang-format | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5216bd7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,39 @@ +--- +Language: Cpp +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignConsecutiveAssignments: + Enabled: true +AlignConsecutiveDeclarations: + Enabled: true +AllowShortFunctionsOnASingleLine: Empty +AlwaysBreakTemplateDeclarations: Yes +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: true +BreakBeforeBraces: Custom +BreakConstructorInitializers: BeforeComma +ColumnLimit: 120 +QualifierAlignment: Left +ConstructorInitializerIndentWidth: 0 +PackConstructorInitializers: Never +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '".*"' + Priority: 1 + - Regex: '<.+>' + Priority: 2 +IndentWidth: 4 +PointerAlignment: Left +... From 6aad7f05bb67f76db8209e190a18790a6a6c905e Mon Sep 17 00:00:00 2001 From: kimci86 Date: Tue, 6 Feb 2024 22:53:40 +0100 Subject: [PATCH 2/4] Format all C++ source files --- include/Arguments.hpp | 325 +++++++++++++------------- include/Attack.hpp | 100 ++++---- include/ConsoleProgress.hpp | 12 +- include/Crc32Tab.hpp | 77 ++++--- include/Data.hpp | 24 +- include/Keys.hpp | 92 ++++---- include/KeystreamTab.hpp | 70 +++--- include/MultTab.hpp | 88 +++---- include/Progress.hpp | 6 +- include/Zip.hpp | 58 +++-- include/Zreduction.hpp | 58 ++--- include/file.hpp | 10 +- include/password.hpp | 123 +++++----- include/types.hpp | 12 +- src/Arguments.cpp | 403 +++++++++++++++++---------------- src/Attack.cpp | 131 +++++------ src/ConsoleProgress.cpp | 54 +++-- src/Crc32Tab.cpp | 8 +- src/Data.cpp | 99 ++++---- src/Keys.cpp | 15 +- src/KeystreamTab.cpp | 4 +- src/MultTab.cpp | 10 +- src/Progress.cpp | 3 +- src/SigintHandler.cpp | 1 + src/VirtualTerminalSupport.cpp | 23 +- src/Zip.cpp | 238 ++++++++++--------- src/Zreduction.cpp | 62 ++--- src/file.cpp | 13 +- src/log.cpp | 8 +- src/main.cpp | 152 +++++++------ src/password.cpp | 223 +++++++++--------- src/types.cpp | 5 +- 32 files changed, 1303 insertions(+), 1204 deletions(-) diff --git a/include/Arguments.hpp b/include/Arguments.hpp index 8341f20..22f5215 100644 --- a/include/Arguments.hpp +++ b/include/Arguments.hpp @@ -1,176 +1,177 @@ #ifndef BKCRACK_ARGUMENTS_HPP #define BKCRACK_ARGUMENTS_HPP +#include "Data.hpp" +#include "Keys.hpp" +#include "types.hpp" + #include #include #include -#include "types.hpp" -#include "Keys.hpp" -#include "Data.hpp" - /// Parse and store arguments class Arguments { +public: + /// Exception thrown if an argument is not valid + class Error : public BaseError + { public: - /// Exception thrown if an argument is not valid - class Error : public BaseError - { - public: - /// Constructor - Error(const std::string& description); - }; - - /// \brief Constructor parsing command line arguments - /// \exception Error if an argument is not valid - Arguments(int argc, const char* argv[]); - - /// \brief Load the data needed for an attack based on parsed arguments - /// \exception FileError if a file cannot be opened - /// \exception ZipError if a zip entry cannot be opened - /// \exception Data::Error if the loaded data cannot be used to carry out an attack - Data loadData() const; - - std::optional cipherFile; ///< File containing the ciphertext - std::optional cipherIndex; ///< Index of the zip entry containing ciphertext - std::optional cipherArchive; ///< Zip archive containing \ref cipherFile - - std::optional plainFile; ///< File containing the known plaintext - std::optional plainIndex; ///< Index of the zip entry containing plaintext - std::optional plainArchive; ///< Zip archive containing \ref plainFile - - /// \brief Maximum number of bytes of plaintext to read from \ref plainFile - /// - /// Set to 1 MiB by default. Using more plaintext is possible, - /// but it uses more RAM and does not speed up the attack much. - std::size_t plainFilePrefix = 1 << 20; - - /// Plaintext offset relative to ciphertext without encryption header (may be negative) - int offset = 0; - - /// Additional bytes of plaintext with their offset relative to ciphertext without encryption header (may be negative) - std::map extraPlaintext; - - /// Tell not to use the check byte derived from ciphertext entry metadata as known plaintext - bool ignoreCheckByte = false; - - /// Staring point of the attack on Z values remaining after reduction - int attackStart = 0; - - /// Password from which to derive the internal password representation - std::optional password; - - /// Internal password representation - std::optional keys; - - /// File to write the deciphered text corresponding to \ref cipherFile - std::optional decipheredFile; - - /// Tell whether to keep the encryption header or discard it when writing the deciphered text - bool keepHeader = false; - - /// Arguments needed to change an archive's password - struct ChangePassword - { - std::string unlockedArchive; ///< File to write the new encrypted archive - std::string newPassword; ///< Password chosen to generate the new archive - }; - /// \copydoc ChangePassword - std::optional changePassword; - - /// \brief Arguments needed to change an archive's internal password representation - /// - /// Changing the internal password representation is an alternative to changing the password - /// when the target password is not known, but its internal representation is known. - struct ChangeKeys - { - std::string unlockedArchive; ///< File to write the new encrypted archive - Keys newKeys; ///< Internal password representation chosen to generate the new archive - }; - /// \copydoc ChangeKeys - std::optional changeKeys; - - /// Characters to generate password candidates - std::optional bruteforce; - - /// Range of password lengths to try during password recovery - struct LengthInterval - { - /// Smallest password length to try (inclusive) - std::size_t minLength{0}; - - /// Greatest password length to try (inclusive) - std::size_t maxLength{std::numeric_limits::max()}; - - /// Compute the intersection between this interval and the given \a other interval - LengthInterval operator&(const LengthInterval& other) const; - }; - /// \copydoc LengthInterval - std::optional length; - - /// Starting point for password recovery - std::string recoveryStart; - - /// Number of threads to use for parallelized operations - int jobs; - - /// Tell whether to try all candidates (keys or passwords) exhaustively or stop after the first success - bool exhaustive = false; - - /// Zip archive about which to display information - std::optional infoArchive; - - /// Tell whether version information is needed or not - bool version = false; - - /// Tell whether help message is needed or not - bool help = false; - - private: - const char** m_current; - const char** const m_end; - - bool finished() const; - - void parseArgument(); - - enum class Option - { - cipherFile, - cipherIndex, - cipherArchive, - plainFile, - plainIndex, - plainArchive, - plainFilePrefix, - offset, - extraPlaintext, - ignoreCheckByte, - attackStart, - password, - keys, - decipheredFile, - keepHeader, - changePassword, - changeKeys, - bruteforce, - length, - recoverPassword, - recoveryStart, - jobs, - exhaustive, - infoArchive, - version, - help - }; - - std::string readString(const std::string& description); - Option readOption(const std::string& description); - int readInt(const std::string& description); - std::size_t readSize(const std::string& description); - bytevec readHex(const std::string& description); - uint32 readKey(const std::string& description); - bytevec readCharset(); + /// Constructor + Error(const std::string& description); + }; + + /// \brief Constructor parsing command line arguments + /// \exception Error if an argument is not valid + Arguments(int argc, const char* argv[]); + + /// \brief Load the data needed for an attack based on parsed arguments + /// \exception FileError if a file cannot be opened + /// \exception ZipError if a zip entry cannot be opened + /// \exception Data::Error if the loaded data cannot be used to carry out an attack + Data loadData() const; + + std::optional cipherFile; ///< File containing the ciphertext + std::optional cipherIndex; ///< Index of the zip entry containing ciphertext + std::optional cipherArchive; ///< Zip archive containing \ref cipherFile + + std::optional plainFile; ///< File containing the known plaintext + std::optional plainIndex; ///< Index of the zip entry containing plaintext + std::optional plainArchive; ///< Zip archive containing \ref plainFile + + /// \brief Maximum number of bytes of plaintext to read from \ref plainFile + /// + /// Set to 1 MiB by default. Using more plaintext is possible, + /// but it uses more RAM and does not speed up the attack much. + std::size_t plainFilePrefix = 1 << 20; + + /// Plaintext offset relative to ciphertext without encryption header (may be negative) + int offset = 0; + + /// Additional bytes of plaintext with their offset relative to ciphertext without encryption header (may be + /// negative) + std::map extraPlaintext; + + /// Tell not to use the check byte derived from ciphertext entry metadata as known plaintext + bool ignoreCheckByte = false; + + /// Staring point of the attack on Z values remaining after reduction + int attackStart = 0; + + /// Password from which to derive the internal password representation + std::optional password; + + /// Internal password representation + std::optional keys; + + /// File to write the deciphered text corresponding to \ref cipherFile + std::optional decipheredFile; + + /// Tell whether to keep the encryption header or discard it when writing the deciphered text + bool keepHeader = false; + + /// Arguments needed to change an archive's password + struct ChangePassword + { + std::string unlockedArchive; ///< File to write the new encrypted archive + std::string newPassword; ///< Password chosen to generate the new archive + }; + /// \copydoc ChangePassword + std::optional changePassword; + + /// \brief Arguments needed to change an archive's internal password representation + /// + /// Changing the internal password representation is an alternative to changing the password + /// when the target password is not known, but its internal representation is known. + struct ChangeKeys + { + std::string unlockedArchive; ///< File to write the new encrypted archive + Keys newKeys; ///< Internal password representation chosen to generate the new archive + }; + /// \copydoc ChangeKeys + std::optional changeKeys; + + /// Characters to generate password candidates + std::optional bruteforce; + + /// Range of password lengths to try during password recovery + struct LengthInterval + { + /// Smallest password length to try (inclusive) + std::size_t minLength{0}; + + /// Greatest password length to try (inclusive) + std::size_t maxLength{std::numeric_limits::max()}; + + /// Compute the intersection between this interval and the given \a other interval + LengthInterval operator&(const LengthInterval& other) const; + }; + /// \copydoc LengthInterval + std::optional length; + + /// Starting point for password recovery + std::string recoveryStart; + + /// Number of threads to use for parallelized operations + int jobs; + + /// Tell whether to try all candidates (keys or passwords) exhaustively or stop after the first success + bool exhaustive = false; + + /// Zip archive about which to display information + std::optional infoArchive; + + /// Tell whether version information is needed or not + bool version = false; + + /// Tell whether help message is needed or not + bool help = false; + +private: + const char** m_current; + const char** const m_end; + + bool finished() const; + + void parseArgument(); + + enum class Option + { + cipherFile, + cipherIndex, + cipherArchive, + plainFile, + plainIndex, + plainArchive, + plainFilePrefix, + offset, + extraPlaintext, + ignoreCheckByte, + attackStart, + password, + keys, + decipheredFile, + keepHeader, + changePassword, + changeKeys, + bruteforce, + length, + recoverPassword, + recoveryStart, + jobs, + exhaustive, + infoArchive, + version, + help + }; + + std::string readString(const std::string& description); + Option readOption(const std::string& description); + int readInt(const std::string& description); + std::size_t readSize(const std::string& description); + bytevec readHex(const std::string& description); + uint32 readKey(const std::string& description); + bytevec readCharset(); }; #endif // BKCRACK_ARGUMENTS_HPP diff --git a/include/Attack.hpp b/include/Attack.hpp index c22281c..fb6ad0b 100644 --- a/include/Attack.hpp +++ b/include/Attack.hpp @@ -1,63 +1,64 @@ #ifndef BKCRACK_ATTACK_HPP #define BKCRACK_ATTACK_HPP -#include - -#include "types.hpp" #include "Data.hpp" #include "Keys.hpp" #include "Progress.hpp" +#include "types.hpp" + +#include /// \file Attack.hpp /// Class to carry out the attack for a given Z[2,32) value class Attack { - public: - /// \brief Constructor - /// \param data Data used to carry out the attack - /// \param index Index of Z[2,32) values passed to carry out the attack - /// \param solutions Shared output vector for valid keys - /// \param solutionsMutex Mutex to protect \a solutions from concurrent access - /// \param exhaustive True to try and find all valid keys, - /// false to stop searching after the first one is found - /// \param progress Object to report progress - Attack(const Data& data, std::size_t index, std::vector& solutions, std::mutex& solutionsMutex, bool exhaustive, Progress& progress); - - /// Carry out the attack for the given Z[2,32) value - void carryout(uint32 z7_2_32); - - enum : std::size_t - { - /// Number of contiguous known plaintext bytes required by the attack - CONTIGUOUS_SIZE = 8, - - /// Total number of known plaintext bytes required by the attack - ATTACK_SIZE = 12 - }; - - private: - // iterate recursively over Z-lists - void exploreZlists(int i); - - // iterate recursively over Y-lists - void exploreYlists(int i); - - // check whether the X-list is valid or not - void testXlist(); - - const Data& data; - - const std::size_t index; // starting index of the used plaintext and keystream - - std::vector& solutions; // shared output vector of valid keys - std::mutex& solutionsMutex; - const bool exhaustive; - Progress& progress; - - u32arr zlist; - u32arr ylist; // the first two elements are not used - u32arr xlist; // the first four elements are not used +public: + /// \brief Constructor + /// \param data Data used to carry out the attack + /// \param index Index of Z[2,32) values passed to carry out the attack + /// \param solutions Shared output vector for valid keys + /// \param solutionsMutex Mutex to protect \a solutions from concurrent access + /// \param exhaustive True to try and find all valid keys, + /// false to stop searching after the first one is found + /// \param progress Object to report progress + Attack(const Data& data, std::size_t index, std::vector& solutions, std::mutex& solutionsMutex, + bool exhaustive, Progress& progress); + + /// Carry out the attack for the given Z[2,32) value + void carryout(uint32 z7_2_32); + + enum : std::size_t + { + /// Number of contiguous known plaintext bytes required by the attack + CONTIGUOUS_SIZE = 8, + + /// Total number of known plaintext bytes required by the attack + ATTACK_SIZE = 12 + }; + +private: + // iterate recursively over Z-lists + void exploreZlists(int i); + + // iterate recursively over Y-lists + void exploreYlists(int i); + + // check whether the X-list is valid or not + void testXlist(); + + const Data& data; + + const std::size_t index; // starting index of the used plaintext and keystream + + std::vector& solutions; // shared output vector of valid keys + std::mutex& solutionsMutex; + const bool exhaustive; + Progress& progress; + + u32arr zlist; + u32arr ylist; // the first two elements are not used + u32arr xlist; // the first four elements are not used }; /// \brief Iterate on Zi[2,32) candidates to try and find complete internal keys @@ -70,6 +71,7 @@ class Attack /// \param exhaustive True to try and find all valid keys, /// false to stop searching after the first one is found /// \param progress Object to report progress -std::vector attack(const Data& data, const u32vec& zi_2_32_vector, int& start, std::size_t index, int jobs, bool exhaustive, Progress& progress); +std::vector attack(const Data& data, const u32vec& zi_2_32_vector, int& start, std::size_t index, int jobs, + bool exhaustive, Progress& progress); #endif // BKCRACK_ATTACK_HPP diff --git a/include/ConsoleProgress.hpp b/include/ConsoleProgress.hpp index d50c093..6109731 100644 --- a/include/ConsoleProgress.hpp +++ b/include/ConsoleProgress.hpp @@ -1,12 +1,12 @@ #ifndef BKCRACK_CONSOLEPROGRESS_HPP #define BKCRACK_CONSOLEPROGRESS_HPP -#include +#include "Progress.hpp" + #include +#include #include -#include "Progress.hpp" - /// Progress indicator which prints itself at regular time intervals class ConsoleProgress : public Progress { @@ -20,12 +20,12 @@ class ConsoleProgress : public Progress private: const std::chrono::milliseconds m_interval; - std::mutex m_in_destructor_mutex; + std::mutex m_in_destructor_mutex; std::condition_variable m_in_destructor_cv; - bool m_in_destructor; + bool m_in_destructor; std::thread m_printer; - void printerFunction(); + void printerFunction(); }; #endif // BKCRACK_CONSOLEPROGRESS_HPP diff --git a/include/Crc32Tab.hpp b/include/Crc32Tab.hpp index cfc5655..226b295 100644 --- a/include/Crc32Tab.hpp +++ b/include/Crc32Tab.hpp @@ -6,43 +6,46 @@ /// Lookup tables for CRC32 related computations class Crc32Tab { - public: - /// \return CRC32 using a lookup table - static inline uint32 crc32(uint32 pval, byte b) - { - return pval >> 8 ^ instance.crctab[lsb(pval) ^ b]; - } - - /// \return CRC32^-1 using a lookup table - static inline uint32 crc32inv(uint32 crc, byte b) - { - return crc << 8 ^ instance.crcinvtab[msb(crc)] ^ b; - } - - /// \return Yi[24,32) from Zi and Z{i-1} using CRC32^-1 - static inline uint32 getYi_24_32(uint32 zi, uint32 zim1) - { - return (crc32inv(zi, 0) ^ zim1) << 24; - } - - /// \return Z{i-1}[10,32) from Zi[2,32) using CRC32^-1 - static inline uint32 getZim1_10_32(uint32 zi_2_32) - { - return crc32inv(zi_2_32, 0) & MASK_10_32; // discard 10 least significant bits - } - - private: - // initialize lookup tables - Crc32Tab(); - - // lookup tables - u32arr<256> crctab; - u32arr<256> crcinvtab; - - // CRC32 polynomial representation - enum : uint32 { CRCPOL = 0xedb88320 }; - - static const Crc32Tab instance; +public: + /// \return CRC32 using a lookup table + static inline uint32 crc32(uint32 pval, byte b) + { + return pval >> 8 ^ instance.crctab[lsb(pval) ^ b]; + } + + /// \return CRC32^-1 using a lookup table + static inline uint32 crc32inv(uint32 crc, byte b) + { + return crc << 8 ^ instance.crcinvtab[msb(crc)] ^ b; + } + + /// \return Yi[24,32) from Zi and Z{i-1} using CRC32^-1 + static inline uint32 getYi_24_32(uint32 zi, uint32 zim1) + { + return (crc32inv(zi, 0) ^ zim1) << 24; + } + + /// \return Z{i-1}[10,32) from Zi[2,32) using CRC32^-1 + static inline uint32 getZim1_10_32(uint32 zi_2_32) + { + return crc32inv(zi_2_32, 0) & MASK_10_32; // discard 10 least significant bits + } + +private: + // initialize lookup tables + Crc32Tab(); + + // lookup tables + u32arr<256> crctab; + u32arr<256> crcinvtab; + + // CRC32 polynomial representation + enum : uint32 + { + CRCPOL = 0xedb88320 + }; + + static const Crc32Tab instance; }; #endif // BKCRACK_CRC32TAB_HPP diff --git a/include/Data.hpp b/include/Data.hpp index 0f8eb6c..7819cdc 100644 --- a/include/Data.hpp +++ b/include/Data.hpp @@ -1,34 +1,38 @@ #ifndef BKCRACK_DATA_HPP #define BKCRACK_DATA_HPP -#include - #include "types.hpp" +#include + /// Structure to hold the data needed for an attack struct Data { - enum : std::size_t { ENCRYPTION_HEADER_SIZE = 12 }; + enum : std::size_t + { + ENCRYPTION_HEADER_SIZE = 12 + }; /// Exception thrown if data cannot be used to carry out an attack class Error : public BaseError { - public: - /// Constructor - Error(const std::string& description); + public: + /// Constructor + Error(const std::string& description); }; /// \brief Construct data and check it can be used to carry out an attack /// \param ciphertext Ciphertext bytes including encryption header /// \param plaintext Plaintext bytes /// \param offset Plaintext offset relative to ciphertext without encryption header (may be negative) - /// \param extraPlaintext Additional bytes of plaintext with their offset relative to ciphertext without encryption header (may be negative) + /// \param extraPlaintext Additional bytes of plaintext with their offset relative to ciphertext without encryption + /// header (may be negative) /// \exception Error if the given data cannot be used to carry out an attack Data(bytevec ciphertext, bytevec plaintext, int offset, const std::map& extraPlaintext); - bytevec ciphertext, ///< ciphertext bytes including encryption header - plaintext, ///< plaintext bytes - keystream; ///< keystream bytes + bytevec ciphertext; ///< ciphertext bytes including encryption header + bytevec plaintext; ///< plaintext bytes + bytevec keystream; ///< keystream bytes /// plaintext and keystream offset relative to ciphertext with encryption header std::size_t offset; diff --git a/include/Keys.hpp b/include/Keys.hpp index e1ca95f..95e9359 100644 --- a/include/Keys.hpp +++ b/include/Keys.hpp @@ -8,57 +8,69 @@ /// Keys defining the cipher state class Keys { - public: - /// Constructor - Keys(uint32 x = 0x12345678, uint32 y = 0x23456789, uint32 z = 0x34567890); +public: + /// Constructor + Keys(uint32 x = 0x12345678, uint32 y = 0x23456789, uint32 z = 0x34567890); - /// Construct keys associated to the given password - Keys(const std::string& password); + /// Construct keys associated to the given password + Keys(const std::string& password); - /// Update the state with a plaintext byte - inline void update(byte p) - { - x = Crc32Tab::crc32(x, p); - y = (y + lsb(x)) * MultTab::MULT + 1; - z = Crc32Tab::crc32(z, msb(y)); - } + /// Update the state with a plaintext byte + inline void update(byte p) + { + x = Crc32Tab::crc32(x, p); + y = (y + lsb(x)) * MultTab::MULT + 1; + z = Crc32Tab::crc32(z, msb(y)); + } - /// Update the state forward to a target offset - void update(const bytevec& ciphertext, std::size_t current, std::size_t target); + /// Update the state forward to a target offset + void update(const bytevec& ciphertext, std::size_t current, std::size_t target); - /// Update the state backward with a ciphertext byte - inline void updateBackward(byte c) - { - z = Crc32Tab::crc32inv(z, msb(y)); - y = (y - 1) * MultTab::MULTINV - lsb(x); - x = Crc32Tab::crc32inv(x, c ^ getK()); - } + /// Update the state backward with a ciphertext byte + inline void updateBackward(byte c) + { + z = Crc32Tab::crc32inv(z, msb(y)); + y = (y - 1) * MultTab::MULTINV - lsb(x); + x = Crc32Tab::crc32inv(x, c ^ getK()); + } - /// Update the state backward with a plaintext byte - inline void updateBackwardPlaintext(byte p) - { - z = Crc32Tab::crc32inv(z, msb(y)); - y = (y - 1) * MultTab::MULTINV - lsb(x); - x = Crc32Tab::crc32inv(x, p); - } + /// Update the state backward with a plaintext byte + inline void updateBackwardPlaintext(byte p) + { + z = Crc32Tab::crc32inv(z, msb(y)); + y = (y - 1) * MultTab::MULTINV - lsb(x); + x = Crc32Tab::crc32inv(x, p); + } - /// Update the state backward to a target offset - void updateBackward(const bytevec& ciphertext, std::size_t current, std::size_t target); + /// Update the state backward to a target offset + void updateBackward(const bytevec& ciphertext, std::size_t current, std::size_t target); - /// \return X value - uint32 getX() const { return x; } + /// \return X value + uint32 getX() const + { + return x; + } - /// \return Y value - uint32 getY() const { return y; } + /// \return Y value + uint32 getY() const + { + return y; + } - /// \return Z value - uint32 getZ() const { return z; } + /// \return Z value + uint32 getZ() const + { + return z; + } - /// \return the keystream byte derived from the keys - byte getK() const { return KeystreamTab::getByte(z); } + /// \return the keystream byte derived from the keys + byte getK() const + { + return KeystreamTab::getByte(z); + } - private: - uint32 x, y, z; +private: + uint32 x, y, z; }; #endif // BKCRACK_KEYS_HPP diff --git a/include/KeystreamTab.hpp b/include/KeystreamTab.hpp index 4622e26..a2de6d3 100644 --- a/include/KeystreamTab.hpp +++ b/include/KeystreamTab.hpp @@ -1,46 +1,46 @@ #ifndef BKCRACK_KEYSTREAMTAB_HPP #define BKCRACK_KEYSTREAMTAB_HPP -#include - #include "types.hpp" +#include + /// Lookup tables for keystream related computations class KeystreamTab { - public: - /// \return the keystream byte ki associated to a Zi value - /// \note Only Zi[2,16) is used - static inline byte getByte(uint32 zi) - { - return instance.keystreamtab[(zi & MASK_0_16) >> 2]; - } - - /// \return a vector of Zi[2,16) values having given [10,16) bits - /// such that getByte(zi) is equal to ki - /// \note the vector contains one element on average - static inline const u32vec& getZi_2_16_vector(byte ki, uint32 zi_10_16) - { - return instance.keystreaminvfiltertab[ki][(zi_10_16 & MASK_0_16) >> 10]; - } - - /// \return true if the vector returned by getZi_2_16_vector is not empty, - /// false otherwise - static inline bool hasZi_2_16(byte ki, uint32 zi_10_16) - { - return instance.keystreaminvexists[ki][(zi_10_16 & MASK_0_16) >> 10]; - } - - private: - // initialize lookup tables - KeystreamTab(); - - // lookup tables - bytearr<1<<14> keystreamtab; - std::array, 256> keystreaminvfiltertab; - std::array, 256> keystreaminvexists; - - static const KeystreamTab instance; +public: + /// \return the keystream byte ki associated to a Zi value + /// \note Only Zi[2,16) is used + static inline byte getByte(uint32 zi) + { + return instance.keystreamtab[(zi & MASK_0_16) >> 2]; + } + + /// \return a vector of Zi[2,16) values having given [10,16) bits + /// such that getByte(zi) is equal to ki + /// \note the vector contains one element on average + static inline const u32vec& getZi_2_16_vector(byte ki, uint32 zi_10_16) + { + return instance.keystreaminvfiltertab[ki][(zi_10_16 & MASK_0_16) >> 10]; + } + + /// \return true if the vector returned by getZi_2_16_vector is not empty, + /// false otherwise + static inline bool hasZi_2_16(byte ki, uint32 zi_10_16) + { + return instance.keystreaminvexists[ki][(zi_10_16 & MASK_0_16) >> 10]; + } + +private: + // initialize lookup tables + KeystreamTab(); + + // lookup tables + bytearr<1 << 14> keystreamtab; + std::array, 256> keystreaminvfiltertab; + std::array, 256> keystreaminvexists; + + static const KeystreamTab instance; }; #endif // BKCRACK_KEYSTREAMTAB_HPP diff --git a/include/MultTab.hpp b/include/MultTab.hpp index ed28660..55a1305 100644 --- a/include/MultTab.hpp +++ b/include/MultTab.hpp @@ -6,50 +6,50 @@ /// Lookup tables for multiplication related computations class MultTab { - public: - /// \return MULT * x using a lookup table - static inline uint32 getMult(byte x) - { - return instance.multtab[x]; - } - - /// \return MULT^-1 * x using a lookup table - static inline uint32 getMultinv(byte x) - { - return instance.multinvtab[x]; - } - - /// \return a vector of bytes x such that - /// msb(x*MULT^-1) is equal to msbprod or msbprod-1 - static inline const bytevec& getMsbProdFiber2(byte msbprodinv) - { - return instance.msbprodfiber2[msbprodinv]; - } - - /// \return a vector of bytes x such that - /// msb(x*MULT^-1) is equal to msbprod, msbprod-1 or msbprod+1 - static inline const bytevec& getMsbProdFiber3(byte msbprodinv) - { - return instance.msbprodfiber3[msbprodinv]; - } - - enum : uint32 - { - MULT = 0x08088405, - MULTINV = 0xd94fa8cd - }; - - private: - // initialize lookup tables - MultTab(); - - // lookup tables - u32arr<256> multtab; - u32arr<256> multinvtab; - std::array msbprodfiber2; - std::array msbprodfiber3; - - static const MultTab instance; +public: + /// \return MULT * x using a lookup table + static inline uint32 getMult(byte x) + { + return instance.multtab[x]; + } + + /// \return MULT^-1 * x using a lookup table + static inline uint32 getMultinv(byte x) + { + return instance.multinvtab[x]; + } + + /// \return a vector of bytes x such that + /// msb(x*MULT^-1) is equal to msbprod or msbprod-1 + static inline const bytevec& getMsbProdFiber2(byte msbprodinv) + { + return instance.msbprodfiber2[msbprodinv]; + } + + /// \return a vector of bytes x such that + /// msb(x*MULT^-1) is equal to msbprod, msbprod-1 or msbprod+1 + static inline const bytevec& getMsbProdFiber3(byte msbprodinv) + { + return instance.msbprodfiber3[msbprodinv]; + } + + enum : uint32 + { + MULT = 0x08088405, + MULTINV = 0xd94fa8cd + }; + +private: + // initialize lookup tables + MultTab(); + + // lookup tables + u32arr<256> multtab; + u32arr<256> multinvtab; + std::array msbprodfiber2; + std::array msbprodfiber3; + + static const MultTab instance; }; #endif // BKCRACK_MULTTAB_HPP diff --git a/include/Progress.hpp b/include/Progress.hpp index f5a7bcd..055fdb3 100644 --- a/include/Progress.hpp +++ b/include/Progress.hpp @@ -30,11 +30,11 @@ class Progress } std::atomic state = State::Normal; ///< State of the long operation - std::atomic done = 0, ///< Number of steps already done - total = 0; ///< Total number of steps + std::atomic done = 0; ///< Number of steps already done + std::atomic total = 0; ///< Total number of steps private: - std::mutex m_os_mutex; + std::mutex m_os_mutex; std::ostream& m_os; }; diff --git a/include/Zip.hpp b/include/Zip.hpp index 0d212a8..17c7eb4 100644 --- a/include/Zip.hpp +++ b/include/Zip.hpp @@ -1,12 +1,12 @@ #ifndef BKCRACK_ZIP_HPP #define BKCRACK_ZIP_HPP -#include -#include - #include "Keys.hpp" #include "Progress.hpp" +#include +#include + /// \brief Open a zip archive, parse zip entries metadata and read raw content /// /// \note Zip64 extensions are supported. @@ -39,11 +39,11 @@ class Zip /// Compression algorithm. \note This enumeration is not exhaustive. enum class Compression { - Store = 0, - Shrink = 1, - Implode = 6, - Deflate = 8, - Deflate64 = 9, + Store = 0, + Shrink = 1, + Implode = 6, + Deflate = 8, + Deflate64 = 9, BZip2 = 12, LZMA = 14, Zstandard = 93, @@ -57,14 +57,14 @@ class Zip /// Information about a zip entry struct Entry { - std::string name; ///< File name - Encryption encryption; ///< Encryption method - Compression compression; ///< Compression method. \note It may take a value not listed in Compression - uint32 crc32; ///< CRC-32 checksum - uint64 offset; ///< Offset of local file header - uint64 packedSize; ///< Packed data size - uint64 uncompressedSize; ///< Uncompressed data size - byte checkByte; ///< Last byte of the encryption header after decryption + std::string name; ///< File name + Encryption encryption; ///< Encryption method + Compression compression; ///< Compression method. \note It may take a value not listed in Compression + uint32 crc32; ///< CRC-32 checksum + uint64 offset; ///< Offset of local file header + uint64 packedSize; ///< Packed data size + uint64 uncompressedSize; ///< Uncompressed data size + byte checkByte; ///< Last byte of the encryption header after decryption }; /// Single-pass input iterator that reads successive Entry objects @@ -89,11 +89,17 @@ class Zip /// \brief Get the current entry /// \pre The iterator must be valid - const Entry& operator*() const { return *m_entry; } + const Entry& operator*() const + { + return *m_entry; + } /// \brief Access a member of the current entry /// \pre The iterator must be valid - const Entry* operator->() const { return &(*m_entry); } + const Entry* operator->() const + { + return &(*m_entry); + } /// \brief Read the next central directory record if any or assign end-of-stream iterator /// \pre The iterator must be valid @@ -115,7 +121,7 @@ class Zip } private: - std::istream* m_is = nullptr; + std::istream* m_is = nullptr; std::optional m_entry; // optional type allows the end-of-stream iterator to be empty }; @@ -129,10 +135,16 @@ class Zip explicit Zip(const std::string& filename); /// Get an iterator pointing to the first entry - Iterator begin() const { return Iterator{*this}; } + Iterator begin() const + { + return Iterator{*this}; + } /// Get an end-of-stream iterator - Iterator end() const { return Iterator{}; } + Iterator end() const + { + return Iterator{}; + } /// \brief Get the first entry having the given name /// \exception Error if the archive does not contain an entry with the given name @@ -160,8 +172,8 @@ class Zip private: std::optional m_file; // optionally own the stream - std::istream& m_is; - const uint64 m_centralDirectoryOffset; + std::istream& m_is; + const uint64 m_centralDirectoryOffset; }; #endif // BKCRACK_ZIP_HPP diff --git a/include/Zreduction.hpp b/include/Zreduction.hpp index fa838ab..0674fd8 100644 --- a/include/Zreduction.hpp +++ b/include/Zreduction.hpp @@ -1,40 +1,40 @@ #ifndef BKCRACK_ZREDUCTION_HPP #define BKCRACK_ZREDUCTION_HPP -#include "types.hpp" #include "Progress.hpp" +#include "types.hpp" /// Generate and reduce Z values class Zreduction { - public: - /// Constructor generating Zi[10,32) values from the last keystream byte - Zreduction(const bytevec& keystream); - - /// Reduce Zi[10,32) number using extra contiguous keystream - void reduce(Progress& progress); - - /// Extend Zi[10,32) values into Zi[2,32) values using keystream - void generate(); - - /// \return the generated Zi[2,32) values - const u32vec& getCandidates() const; - - /// \return the index of the Zi[2,32) values relative to keystream - std::size_t getIndex() const; - - private: - enum : std::size_t - { - WAIT_SIZE = 1 << 8, - TRACK_SIZE = 1 << 16 - }; - - const bytevec& keystream; - // After constructor or reduce(), contains Z[10,32) values. - // After generate(), contains Zi[2,32) values. - u32vec zi_vector; - std::size_t index; +public: + /// Constructor generating Zi[10,32) values from the last keystream byte + Zreduction(const bytevec& keystream); + + /// Reduce Zi[10,32) number using extra contiguous keystream + void reduce(Progress& progress); + + /// Extend Zi[10,32) values into Zi[2,32) values using keystream + void generate(); + + /// \return the generated Zi[2,32) values + const u32vec& getCandidates() const; + + /// \return the index of the Zi[2,32) values relative to keystream + std::size_t getIndex() const; + +private: + enum : std::size_t + { + WAIT_SIZE = 1 << 8, + TRACK_SIZE = 1 << 16 + }; + + const bytevec& keystream; + // After constructor or reduce(), contains Z[10,32) values. + // After generate(), contains Zi[2,32) values. + u32vec zi_vector; + std::size_t index; }; #endif // BKCRACK_ZREDUCTION_HPP diff --git a/include/file.hpp b/include/file.hpp index 3b50810..ce8874b 100644 --- a/include/file.hpp +++ b/include/file.hpp @@ -22,16 +22,16 @@ /// } /// \enddot -#include - #include "types.hpp" +#include + /// Exception thrown if a file cannot be opened class FileError : public BaseError { - public: - /// Constructor - FileError(const std::string& description); +public: + /// Constructor + FileError(const std::string& description); }; /// \brief Open an input file stream diff --git a/include/password.hpp b/include/password.hpp index 18b5b67..40c8445 100644 --- a/include/password.hpp +++ b/include/password.hpp @@ -1,73 +1,74 @@ #ifndef BKCRACK_PASSWORD_HPP #define BKCRACK_PASSWORD_HPP -#include -#include - #include "Keys.hpp" #include "Progress.hpp" +#include +#include + /// \file password.hpp /// Class to recover a password from internal keys class Recovery { - public: - /// Constructor - Recovery(const Keys& keys, const bytevec& charset, std::vector& solutions, std::mutex& solutionsMutex, bool exhaustive, Progress& progress); - - /// \brief Look for a password of length 6 or less - /// - /// Try to derive 6 characters such that updating the given cipher state - /// with them gives the target cipher state (the password representation). - /// On success, the current prefix followed by those 6 characters is added - /// to the shared output vector. - /// - /// If the target length is less than 6, the first characters are ignored so that - /// the saved solution has the target length. This is useful to recover passwords - /// shorter than 6 characters. - /// - /// \pre prefix.size() + 6 == length && initial == Keys{prefix} || length < 6 - void recoverShortPassword(const Keys& initial); - - /// \brief Look for password of length 7 or more - /// - /// Recursively iterate on possible prefixes of length-6 characters. - /// For each prefix, try to recover the last 6 characters like recoverShortPassword. - /// - /// \pre prefix.size() + 6 < length && initial == Keys{prefix} - void recoverLongPassword(const Keys& initial); - - /// Length of the password to recover - std::size_t length; - - /// The first characters of the password candidate, up to length-6 characters long - std::string prefix; - - /// Set of characters to generate password candidates - const bytevec& charset; - - private: - // iterate recursively on possible Y values - void recursion(int i); - - // set of possible Z0[16,31) values considering given character set - std::bitset<1<<16> z0_16_32; - - // set of possible Z{-1}[24,32) values considering given character set - std::bitset<1<<8> zm1_24_32; - - // cipher state (X,Y,Z)_i for index i in [0, 6] where the last state (X,Y,Z)_6 is - // the representation of the password to recover - u32arr<7> x, y, z; - uint32 x0; // backup of candidate X value for convenience - - bytearr<6> p; // password last 6 bytes - - std::vector& solutions; // shared output vector of valid passwords - std::mutex& solutionsMutex; - const bool exhaustive; - Progress& progress; +public: + /// Constructor + Recovery(const Keys& keys, const bytevec& charset, std::vector& solutions, std::mutex& solutionsMutex, + bool exhaustive, Progress& progress); + + /// \brief Look for a password of length 6 or less + /// + /// Try to derive 6 characters such that updating the given cipher state + /// with them gives the target cipher state (the password representation). + /// On success, the current prefix followed by those 6 characters is added + /// to the shared output vector. + /// + /// If the target length is less than 6, the first characters are ignored so that + /// the saved solution has the target length. This is useful to recover passwords + /// shorter than 6 characters. + /// + /// \pre prefix.size() + 6 == length && initial == Keys{prefix} || length < 6 + void recoverShortPassword(const Keys& initial); + + /// \brief Look for password of length 7 or more + /// + /// Recursively iterate on possible prefixes of length-6 characters. + /// For each prefix, try to recover the last 6 characters like recoverShortPassword. + /// + /// \pre prefix.size() + 6 < length && initial == Keys{prefix} + void recoverLongPassword(const Keys& initial); + + /// Length of the password to recover + std::size_t length; + + /// The first characters of the password candidate, up to length-6 characters long + std::string prefix; + + /// Set of characters to generate password candidates + const bytevec& charset; + +private: + // iterate recursively on possible Y values + void recursion(int i); + + // set of possible Z0[16,31) values considering given character set + std::bitset<1 << 16> z0_16_32; + + // set of possible Z{-1}[24,32) values considering given character set + std::bitset<1 << 8> zm1_24_32; + + // cipher state (X,Y,Z)_i for index i in [0, 6] where the last state (X,Y,Z)_6 is + // the representation of the password to recover + u32arr<7> x, y, z; + uint32 x0; // backup of candidate X value for convenience + + bytearr<6> p; // password last 6 bytes + + std::vector& solutions; // shared output vector of valid passwords + std::mutex& solutionsMutex; + const bool exhaustive; + Progress& progress; }; /// \brief Try to recover the password associated with the given keys @@ -84,6 +85,8 @@ class Recovery /// \return A vector of passwords associated with the given keys. /// A vector is needed instead of a single string because there can be /// collisions (i.e. several passwords for the same keys). -std::vector recoverPassword(const Keys& keys, const bytevec& charset, std::size_t minLength, std::size_t maxLength, std::string& start, int jobs, bool exhaustive, Progress& progress); +std::vector recoverPassword(const Keys& keys, const bytevec& charset, std::size_t minLength, + std::size_t maxLength, std::string& start, int jobs, bool exhaustive, + Progress& progress); #endif // BKCRACK_PASSWORD_HPP diff --git a/include/types.hpp b/include/types.hpp index 4fbca24..d7b9658 100644 --- a/include/types.hpp +++ b/include/types.hpp @@ -4,11 +4,11 @@ /// \file types.hpp /// \brief Typedefs, useful constants and utility functions -#include -#include -#include #include +#include +#include #include +#include /// Base exception type class BaseError : public std::runtime_error @@ -20,7 +20,7 @@ class BaseError : public std::runtime_error // scalar types -using byte = std::uint8_t; ///< Unsigned integer type with width of exactly 8 bits +using byte = std::uint8_t; ///< Unsigned integer type with width of exactly 8 bits using uint16 = std::uint16_t; ///< Unsigned integer type with width of exactly 16 bits using uint32 = std::uint32_t; ///< Unsigned integer type with width of exactly 32 bits using uint64 = std::uint64_t; ///< Unsigned integer type with width of exactly 64 bits @@ -33,8 +33,8 @@ using bytearr = std::array; ///< Array of bytes template using u32arr = std::array; ///< Array of 32-bits integers -using bytevec = std::vector; ///< Vector of bytes -using u32vec = std::vector; ///< Vector of 32-bits integers +using bytevec = std::vector; ///< Vector of bytes +using u32vec = std::vector; ///< Vector of 32-bits integers // utility functions diff --git a/src/Arguments.cpp b/src/Arguments.cpp index 06e8934..b18d4a9 100644 --- a/src/Arguments.cpp +++ b/src/Arguments.cpp @@ -1,6 +1,8 @@ #include "Arguments.hpp" -#include "file.hpp" + #include "Zip.hpp" +#include "file.hpp" + #include #include #include @@ -17,7 +19,7 @@ std::bitset<256> charRange(char first, char last) do { bitset.set(first); - } while(first++ != last); + } while (first++ != last); return bitset; } @@ -29,44 +31,38 @@ auto translateIntParseError(F&& f, const std::string& value) { return f(value); } - catch(const std::invalid_argument&) + catch (const std::invalid_argument&) { - throw Arguments::Error("expected an integer, got \""+value+"\""); + throw Arguments::Error("expected an integer, got \"" + value + "\""); } - catch(const std::out_of_range&) + catch (const std::out_of_range&) { - throw Arguments::Error("integer value "+value+" is out of range"); + throw Arguments::Error("integer value " + value + " is out of range"); } } int parseInt(const std::string& value) { - return translateIntParseError([](const std::string& value) - { - return std::stoi(value, nullptr, 0); - }, value); + return translateIntParseError([](const std::string& value) { return std::stoi(value, nullptr, 0); }, value); } std::size_t parseSize(const std::string& value) { - return translateIntParseError([](const std::string& value) - { - return std::stoull(value, nullptr, 0); - }, value); + return translateIntParseError([](const std::string& value) { return std::stoull(value, nullptr, 0); }, value); } std::variant parseInterval(const std::string& value) { const std::string separator = ".."; - if(const auto minEnd = value.find(separator); minEnd != std::string::npos) + if (const auto minEnd = value.find(separator); minEnd != std::string::npos) { Arguments::LengthInterval interval; - if(0 < minEnd) + if (0 < minEnd) interval.minLength = parseSize(value.substr(0, minEnd)); - if(const auto maxBegin = minEnd + separator.size(); maxBegin < value.size()) + if (const auto maxBegin = minEnd + separator.size(); maxBegin < value.size()) interval.maxLength = parseSize(value.substr(maxBegin)); return interval; @@ -79,69 +75,73 @@ std::variant parseInterval(const std::st Arguments::Error::Error(const std::string& description) : BaseError("Arguments error", description) -{} +{ +} Arguments::Arguments(int argc, const char* argv[]) -: jobs{[]() -> int { - const auto concurrency = std::thread::hardware_concurrency(); - return concurrency ? concurrency : 2; - }()} -, m_current{argv + 1}, m_end{argv + argc} +: jobs{[]() -> int + { + const auto concurrency = std::thread::hardware_concurrency(); + return concurrency ? concurrency : 2; + }()} +, m_current{argv + 1} +, m_end{argv + argc} { // parse arguments - while(!finished()) + while (!finished()) parseArgument(); - if(help || version || infoArchive) + if (help || version || infoArchive) return; // no further checks are needed for those options // check constraints on arguments - if(keys) + if (keys) { - if(!decipheredFile && !changePassword && !changeKeys && !bruteforce) + if (!decipheredFile && !changePassword && !changeKeys && !bruteforce) throw Error("-d, -U, --change-keys or --bruteforce parameter is missing (required by -k)"); } - else if(!password) + else if (!password) { - if(cipherFile && cipherIndex) + if (cipherFile && cipherIndex) throw Error("-c and --cipher-index cannot be used at the same time"); - if(plainFile && plainIndex) + if (plainFile && plainIndex) throw Error("-p and --plain-index cannot be used at the same time"); - if(!cipherFile && !cipherIndex) + if (!cipherFile && !cipherIndex) throw Error("-c or --cipher-index parameter is missing"); - if(!plainFile && !plainIndex && extraPlaintext.empty()) + if (!plainFile && !plainIndex && extraPlaintext.empty()) throw Error("-p, --plain-index or -x parameter is missing"); - if(plainArchive && !plainFile && !plainIndex) + if (plainArchive && !plainFile && !plainIndex) throw Error("-p or --plain-index parameter is missing (required by -P)"); - if(cipherIndex && !cipherArchive) + if (cipherIndex && !cipherArchive) throw Error("-C parameter is missing (required by --cipher-index)"); - if(plainIndex && !plainArchive) + if (plainIndex && !plainArchive) throw Error("-P parameter is missing (required by --plain-index)"); constexpr int minimumOffset = -static_cast(Data::ENCRYPTION_HEADER_SIZE); - if(offset < minimumOffset) - throw Error("plaintext offset "+std::to_string(offset)+" is too small (minimum is "+std::to_string(minimumOffset)+")"); + if (offset < minimumOffset) + throw Error("plaintext offset " + std::to_string(offset) + " is too small (minimum is " + + std::to_string(minimumOffset) + ")"); } - if(decipheredFile && !cipherFile && !cipherIndex) + if (decipheredFile && !cipherFile && !cipherIndex) throw Error("-c or --cipher-index parameter is missing (required by -d)"); - if(decipheredFile && !cipherArchive && decipheredFile == cipherFile) + if (decipheredFile && !cipherArchive && decipheredFile == cipherFile) throw Error("-c and -d parameters must point to different files"); - if(changePassword && !cipherArchive) + if (changePassword && !cipherArchive) throw Error("-C parameter is missing (required by -U)"); - if(changePassword && changePassword->unlockedArchive == cipherArchive) + if (changePassword && changePassword->unlockedArchive == cipherArchive) throw Error("-C and -U parameters must point to different files"); - if(changeKeys && !cipherArchive) + if (changeKeys && !cipherArchive) throw Error("-C parameter is missing (required by --change-keys)"); - if(changeKeys && changeKeys->unlockedArchive == cipherArchive) + if (changeKeys && changeKeys->unlockedArchive == cipherArchive) throw Error("-C and --change-keys parameters must point to different files"); - if(length && !bruteforce) + if (length && !bruteforce) throw Error("--bruteforce parameter is missing (required by --length)"); } @@ -149,42 +149,43 @@ Data Arguments::loadData() const { // load known plaintext bytevec plaintext; - if(plainArchive) + if (plainArchive) { const auto archive = Zip{*plainArchive}; - const auto entry = plainFile ? archive[*plainFile] : archive[*plainIndex]; + const auto entry = plainFile ? archive[*plainFile] : archive[*plainIndex]; Zip::checkEncryption(entry, Zip::Encryption::None); plaintext = archive.load(entry, plainFilePrefix); } - else if(plainFile) + else if (plainFile) plaintext = loadFile(*plainFile, plainFilePrefix); // load ciphertext needed by the attack std::size_t needed = Data::ENCRYPTION_HEADER_SIZE; - if(!plaintext.empty()) + if (!plaintext.empty()) needed = std::max(needed, Data::ENCRYPTION_HEADER_SIZE + offset + plaintext.size()); - if(!extraPlaintext.empty()) + if (!extraPlaintext.empty()) needed = std::max(needed, Data::ENCRYPTION_HEADER_SIZE + extraPlaintext.rbegin()->first + 1); - bytevec ciphertext; + bytevec ciphertext; std::optional> extraPlaintextWithCheckByte; - if(cipherArchive) + if (cipherArchive) { const auto archive = Zip{*cipherArchive}; - const auto entry = cipherFile ? archive[*cipherFile] : archive[*cipherIndex]; + const auto entry = cipherFile ? archive[*cipherFile] : archive[*cipherIndex]; Zip::checkEncryption(entry, Zip::Encryption::Traditional); ciphertext = archive.load(entry, needed); - if(!ignoreCheckByte && !extraPlaintext.count(-1)) + if (!ignoreCheckByte && !extraPlaintext.count(-1)) { - extraPlaintextWithCheckByte = extraPlaintext; + extraPlaintextWithCheckByte = extraPlaintext; (*extraPlaintextWithCheckByte)[-1] = entry.checkByte; } } else ciphertext = loadFile(*cipherFile, needed); - return Data(std::move(ciphertext), std::move(plaintext), offset, extraPlaintextWithCheckByte.value_or(extraPlaintext)); + return Data(std::move(ciphertext), std::move(plaintext), offset, + extraPlaintextWithCheckByte.value_or(extraPlaintext)); } Arguments::LengthInterval Arguments::LengthInterval::operator&(const Arguments::LengthInterval& other) const @@ -199,121 +200,128 @@ bool Arguments::finished() const void Arguments::parseArgument() { - switch(readOption("an option")) + switch (readOption("an option")) { - case Option::cipherFile: - cipherFile = readString("ciphertext"); - break; - case Option::cipherIndex: - cipherIndex = readSize("index"); - break; - case Option::cipherArchive: - cipherArchive = readString("encryptedzip"); - break; - case Option::plainFile: - plainFile = readString("plaintext"); - break; - case Option::plainIndex: - plainIndex = readSize("index"); - break; - case Option::plainArchive: - plainArchive = readString("plainzip"); - break; - case Option::plainFilePrefix: - plainFilePrefix = readSize("size"); - break; - case Option::offset: - offset = readInt("offset"); - break; - case Option::extraPlaintext: - { - int i = readInt("offset"); - for(byte b : readHex("data")) - extraPlaintext[i++] = b; - break; - } - case Option::ignoreCheckByte: - ignoreCheckByte = true; - break; - case Option::attackStart: - attackStart = readInt("checkpoint"); - break; - case Option::password: - password = readString("password"); - break; - case Option::keys: - keys = {readKey("X"), readKey("Y"), readKey("Z")}; - break; - case Option::decipheredFile: - decipheredFile = readString("decipheredfile"); - break; - case Option::keepHeader: - keepHeader = true; - break; - case Option::changePassword: - changePassword = {readString("unlockedzip"), readString("password")}; - break; - case Option::changeKeys: - changeKeys = {readString("unlockedzip"), Keys{readKey("X"), readKey("Y"), readKey("Z")}}; - break; - case Option::bruteforce: - bruteforce = readCharset(); - break; - case Option::length: - length = length.value_or(LengthInterval{}) & std::visit([](auto arg) - { - if constexpr(std::is_same_v) - return LengthInterval{arg, arg}; // a single value is interpreted as an exact length - else - return arg; - }, parseInterval(readString("length"))); - break; - case Option::recoverPassword: - length = length.value_or(LengthInterval{}) & std::visit([](auto arg) - { - if constexpr(std::is_same_v) - return LengthInterval{0, arg}; // a single value is interpreted as an interval 0..max - else - return arg; - }, parseInterval(readString("length"))); - bruteforce = readCharset(); - break; - case Option::recoveryStart: - { - const bytevec checkpoint = readHex("checkpoint"); - recoveryStart.assign(checkpoint.begin(), checkpoint.end()); - break; - } - case Option::jobs: - jobs = readInt("count"); - break; - case Option::exhaustive: - exhaustive = true; - break; - case Option::infoArchive: - infoArchive = readString("zipfile"); - break; - case Option::version: - version = true; - break; - case Option::help: - help = true; - break; + case Option::cipherFile: + cipherFile = readString("ciphertext"); + break; + case Option::cipherIndex: + cipherIndex = readSize("index"); + break; + case Option::cipherArchive: + cipherArchive = readString("encryptedzip"); + break; + case Option::plainFile: + plainFile = readString("plaintext"); + break; + case Option::plainIndex: + plainIndex = readSize("index"); + break; + case Option::plainArchive: + plainArchive = readString("plainzip"); + break; + case Option::plainFilePrefix: + plainFilePrefix = readSize("size"); + break; + case Option::offset: + offset = readInt("offset"); + break; + case Option::extraPlaintext: + { + int i = readInt("offset"); + for (byte b : readHex("data")) + extraPlaintext[i++] = b; + break; + } + case Option::ignoreCheckByte: + ignoreCheckByte = true; + break; + case Option::attackStart: + attackStart = readInt("checkpoint"); + break; + case Option::password: + password = readString("password"); + break; + case Option::keys: + keys = {readKey("X"), readKey("Y"), readKey("Z")}; + break; + case Option::decipheredFile: + decipheredFile = readString("decipheredfile"); + break; + case Option::keepHeader: + keepHeader = true; + break; + case Option::changePassword: + changePassword = {readString("unlockedzip"), readString("password")}; + break; + case Option::changeKeys: + changeKeys = {readString("unlockedzip"), Keys{readKey("X"), readKey("Y"), readKey("Z")}}; + break; + case Option::bruteforce: + bruteforce = readCharset(); + break; + case Option::length: + length = length.value_or(LengthInterval{}) & + std::visit( + [](auto arg) + { + if constexpr (std::is_same_v) + return LengthInterval{arg, arg}; // a single value is interpreted as an exact length + else + return arg; + }, + parseInterval(readString("length"))); + break; + case Option::recoverPassword: + length = length.value_or(LengthInterval{}) & + std::visit( + [](auto arg) + { + if constexpr (std::is_same_v) + return LengthInterval{0, arg}; // a single value is interpreted as an interval 0..max + else + return arg; + }, + parseInterval(readString("length"))); + bruteforce = readCharset(); + break; + case Option::recoveryStart: + { + const bytevec checkpoint = readHex("checkpoint"); + recoveryStart.assign(checkpoint.begin(), checkpoint.end()); + break; + } + case Option::jobs: + jobs = readInt("count"); + break; + case Option::exhaustive: + exhaustive = true; + break; + case Option::infoArchive: + infoArchive = readString("zipfile"); + break; + case Option::version: + version = true; + break; + case Option::help: + help = true; + break; } } std::string Arguments::readString(const std::string& description) { - if(finished()) - throw Error("expected "+description+", got nothing"); + if (finished()) + throw Error("expected " + description + ", got nothing"); return *m_current++; } Arguments::Option Arguments::readOption(const std::string& description) { - #define PAIR(string, option) {#string, Option::option} - #define PAIRS(short, long, option) PAIR(short, option), PAIR(long, option) + // clang-format off +#define PAIR(string, option) {#string, Option::option} +#define PAIRS(short, long, option) PAIR(short, option), PAIR(long, option) static const std::map stringToOption = { PAIRS(-c, --cipher-file, cipherFile), @@ -341,15 +349,16 @@ Arguments::Option Arguments::readOption(const std::string& description) PAIRS(-e, --exhaustive, exhaustive), PAIRS(-L, --list, infoArchive), PAIR ( --version, version), - PAIRS(-h, --help, help) + PAIRS(-h, --help, help), }; + // clang-format on - #undef PAIR - #undef PAIRS +#undef PAIR +#undef PAIRS std::string str = readString(description); - if(auto it = stringToOption.find(str); it == stringToOption.end()) - throw Error("unknown option "+str); + if (auto it = stringToOption.find(str); it == stringToOption.end()) + throw Error("unknown option " + str); else return it->second; } @@ -368,13 +377,13 @@ bytevec Arguments::readHex(const std::string& description) { std::string str = readString(description); - if(str.size() % 2) - throw Error("expected an even-length string, got "+str); - if(!std::all_of(str.begin(), str.end(), [](unsigned char c){ return std::isxdigit(c); })) - throw Error("expected "+description+" in hexadecimal, got "+str); + if (str.size() % 2) + throw Error("expected an even-length string, got " + str); + if (!std::all_of(str.begin(), str.end(), [](unsigned char c) { return std::isxdigit(c); })) + throw Error("expected " + description + " in hexadecimal, got " + str); bytevec data; - for(std::size_t i = 0; i < str.length(); i += 2) + for (std::size_t i = 0; i < str.length(); i += 2) data.push_back(static_cast(std::stoul(str.substr(i, 2), nullptr, 16))); return data; @@ -384,53 +393,53 @@ uint32 Arguments::readKey(const std::string& description) { std::string str = readString(description); - if(str.size() > 8) - throw Error("expected a string of length 8 or less, got "+str); - if(!std::all_of(str.begin(), str.end(), [](unsigned char c){ return std::isxdigit(c); })) - throw Error("expected "+description+" in hexadecimal, got "+str); + if (str.size() > 8) + throw Error("expected a string of length 8 or less, got " + str); + if (!std::all_of(str.begin(), str.end(), [](unsigned char c) { return std::isxdigit(c); })) + throw Error("expected " + description + " in hexadecimal, got " + str); return static_cast(std::stoul(str, nullptr, 16)); } bytevec Arguments::readCharset() { - const std::bitset<256> - lowercase = charRange('a', 'z'), - uppercase = charRange('A', 'Z'), - digits = charRange('0', '9'), - alphanum = lowercase | uppercase | digits, - printable = charRange(' ', '~'), - punctuation = printable & ~alphanum; + const std::bitset<256> lowercase = charRange('a', 'z'); + const std::bitset<256> uppercase = charRange('A', 'Z'); + const std::bitset<256> digits = charRange('0', '9'); + const std::bitset<256> alphanum = lowercase | uppercase | digits; + const std::bitset<256> printable = charRange(' ', '~'); + const std::bitset<256> punctuation = printable & ~alphanum; const std::string charsetArg = readString("charset"); - if(charsetArg.empty()) + if (charsetArg.empty()) throw Error("the charset for password recovery is empty"); std::bitset<256> charset; - for(auto it = charsetArg.begin(); it != charsetArg.end(); ++it) + for (auto it = charsetArg.begin(); it != charsetArg.end(); ++it) { - if(*it == '?') // escape character for predefined charsets + if (*it == '?') // escape character for predefined charsets { - if(++it == charsetArg.end()) + if (++it == charsetArg.end()) { charset.set('?'); break; } - switch(*it) + switch (*it) { - case 'l': charset |= lowercase; break; - case 'u': charset |= uppercase; break; - case 'd': charset |= digits; break; - case 's': charset |= punctuation; break; - case 'a': charset |= alphanum; break; - case 'p': charset |= printable; break; - case 'b': charset.set(); break; - case '?': charset.set('?'); break; - - default: - throw Error(std::string("unknown charset ?")+*it); + // clang-format off + case 'l': charset |= lowercase; break; + case 'u': charset |= uppercase; break; + case 'd': charset |= digits; break; + case 's': charset |= punctuation; break; + case 'a': charset |= alphanum; break; + case 'p': charset |= printable; break; + case 'b': charset.set(); break; + case '?': charset.set('?'); break; + // clang-format on + default: + throw Error(std::string("unknown charset ?") + *it); } } else @@ -438,8 +447,8 @@ bytevec Arguments::readCharset() } bytevec result; - for(int c = 0; c < 256; c++) - if(charset[c]) + for (int c = 0; c < 256; c++) + if (charset[c]) result.push_back(c); return result; diff --git a/src/Attack.cpp b/src/Attack.cpp index 100e05a..a7e42e8 100644 --- a/src/Attack.cpp +++ b/src/Attack.cpp @@ -1,15 +1,24 @@ #include "Attack.hpp" -#include "log.hpp" + #include "Crc32Tab.hpp" #include "KeystreamTab.hpp" #include "MultTab.hpp" +#include "log.hpp" + #include #include #include -Attack::Attack(const Data& data, std::size_t index, std::vector& solutions, std::mutex& solutionsMutex, bool exhaustive, Progress& progress) - : data(data), index(index + 1 - Attack::CONTIGUOUS_SIZE), solutions(solutions), solutionsMutex(solutionsMutex), exhaustive(exhaustive), progress(progress) -{} +Attack::Attack(const Data& data, std::size_t index, std::vector& solutions, std::mutex& solutionsMutex, + bool exhaustive, Progress& progress) +: data(data) +, index(index + 1 - Attack::CONTIGUOUS_SIZE) +, solutions(solutions) +, solutionsMutex(solutionsMutex) +, exhaustive(exhaustive) +, progress(progress) +{ +} void Attack::carryout(uint32 z7_2_32) { @@ -19,38 +28,37 @@ void Attack::carryout(uint32 z7_2_32) void Attack::exploreZlists(int i) { - if(i != 0) // the Z-list is not complete so generate Z{i-1}[2,32) values + if (i != 0) // the Z-list is not complete so generate Z{i-1}[2,32) values { // get Z{i-1}[10,32) from CRC32^-1 uint32 zim1_10_32 = Crc32Tab::getZim1_10_32(zlist[i]); // get Z{i-1}[2,16) values from keystream byte k{i-1} and Z{i-1}[10,16) - for(uint32 zim1_2_16 : KeystreamTab::getZi_2_16_vector(data.keystream[index+i-1], zim1_10_32)) + for (uint32 zim1_2_16 : KeystreamTab::getZi_2_16_vector(data.keystream[index + i - 1], zim1_10_32)) { // add Z{i-1}[2,32) to the Z-list - zlist[i-1] = zim1_10_32 | zim1_2_16; + zlist[i - 1] = zim1_10_32 | zim1_2_16; // find Zi[0,2) from CRC32^1 zlist[i] &= MASK_2_32; // discard 2 least significant bits - zlist[i] |= (Crc32Tab::crc32inv(zlist[i], 0) ^ zlist[i-1]) >> 8; + zlist[i] |= (Crc32Tab::crc32inv(zlist[i], 0) ^ zlist[i - 1]) >> 8; // get Y{i+1}[24,32) - if(i < 7) - ylist[i+1] = Crc32Tab::getYi_24_32(zlist[i+1], zlist[i]); + if (i < 7) + ylist[i + 1] = Crc32Tab::getYi_24_32(zlist[i + 1], zlist[i]); - exploreZlists(i-1); + exploreZlists(i - 1); } } else // the Z-list is complete so iterate over possible Y values { // guess Y7[8,24) and keep prod == (Y7[8,32) - 1) * mult^-1 - for(uint32 y7_8_24 = 0, prod = (MultTab::getMultinv(msb(ylist[7])) << 24) - MultTab::MULTINV; - y7_8_24 < 1 << 24; - y7_8_24 += 1 << 8, prod += MultTab::MULTINV << 8) + for (uint32 y7_8_24 = 0, prod = (MultTab::getMultinv(msb(ylist[7])) << 24) - MultTab::MULTINV; + y7_8_24 < 1 << 24; y7_8_24 += 1 << 8, prod += MultTab::MULTINV << 8) // get possible Y7[0,8) values - for(byte y7_0_8 : MultTab::getMsbProdFiber3(msb(ylist[6]) - msb(prod))) + for (byte y7_0_8 : MultTab::getMsbProdFiber3(msb(ylist[6]) - msb(prod))) // filter Y7[0,8) using Y6[24,32) - if(prod + MultTab::getMultinv(y7_0_8) - (ylist[6] & MASK_24_32) <= MAXDIFF_0_24) + if (prod + MultTab::getMultinv(y7_0_8) - (ylist[6] & MASK_24_32) <= MAXDIFF_0_24) { ylist[7] = y7_0_8 | y7_8_24 | (ylist[7] & MASK_24_32); exploreYlists(7); @@ -60,28 +68,28 @@ void Attack::exploreZlists(int i) void Attack::exploreYlists(int i) { - if(i != 3) // the Y-list is not complete so generate Y{i-1} values + if (i != 3) // the Y-list is not complete so generate Y{i-1} values { - uint32 fy = (ylist[i] - 1) * MultTab::MULTINV; + uint32 fy = (ylist[i] - 1) * MultTab::MULTINV; uint32 ffy = (fy - 1) * MultTab::MULTINV; // get possible LSB(Xi) - for(byte xi_0_8 : MultTab::getMsbProdFiber2(msb(ffy - (ylist[i-2] & MASK_24_32)))) + for (byte xi_0_8 : MultTab::getMsbProdFiber2(msb(ffy - (ylist[i - 2] & MASK_24_32)))) { // compute corresponding Y{i-1} uint32 yim1 = fy - xi_0_8; // filter values with Y{i-2}[24,32) - if(ffy - MultTab::getMultinv(xi_0_8) - (ylist[i-2] & MASK_24_32) <= MAXDIFF_0_24 - && msb(yim1) == msb(ylist[i-1])) + if (ffy - MultTab::getMultinv(xi_0_8) - (ylist[i - 2] & MASK_24_32) <= MAXDIFF_0_24 && + msb(yim1) == msb(ylist[i - 1])) { // add Y{i-1} to the Y-list - ylist[i-1] = yim1; + ylist[i - 1] = yim1; // set Xi value xlist[i] = xi_0_8; - exploreYlists(i-1); + exploreYlists(i - 1); } } } @@ -92,30 +100,28 @@ void Attack::exploreYlists(int i) void Attack::testXlist() { // compute X7 - for(int i = 5; i <= 7; i++) - xlist[i] = (Crc32Tab::crc32(xlist[i-1], data.plaintext[index+i-1]) - & MASK_8_32) // discard the LSB - | lsb(xlist[i]); // set the LSB + for (int i = 5; i <= 7; i++) + xlist[i] = (Crc32Tab::crc32(xlist[i - 1], data.plaintext[index + i - 1]) & MASK_8_32) // discard the LSB + | lsb(xlist[i]); // set the LSB // compute X3 uint32 x = xlist[7]; - for(int i = 6; i >= 3; i--) - x = Crc32Tab::crc32inv(x, data.plaintext[index+i]); + for (int i = 6; i >= 3; i--) + x = Crc32Tab::crc32inv(x, data.plaintext[index + i]); // check that X3 fits with Y1[26,32) uint32 y1_26_32 = Crc32Tab::getYi_24_32(zlist[1], zlist[0]) & MASK_26_32; - if(((ylist[3] - 1) * MultTab::MULTINV - lsb(x) - 1) * MultTab::MULTINV - y1_26_32 > MAXDIFF_0_26) + if (((ylist[3] - 1) * MultTab::MULTINV - lsb(x) - 1) * MultTab::MULTINV - y1_26_32 > MAXDIFF_0_26) return; // decipher and filter by comparing with remaining contiguous plaintext forward Keys keysForward(xlist[7], ylist[7], zlist[7]); - keysForward.update(data.plaintext[index+7]); - for(bytevec::const_iterator p = data.plaintext.begin() + index + 8, - c = data.ciphertext.begin() + data.offset + index + 8; - p != data.plaintext.end(); - ++p, ++c) + keysForward.update(data.plaintext[index + 7]); + for (bytevec::const_iterator p = data.plaintext.begin() + index + 8, + c = data.ciphertext.begin() + data.offset + index + 8; + p != data.plaintext.end(); ++p, ++c) { - if((*c ^ keysForward.getK()) != *p) + if ((*c ^ keysForward.getK()) != *p) return; keysForward.update(*p); } @@ -125,36 +131,34 @@ void Attack::testXlist() // and also backward Keys keysBackward(x, ylist[3], zlist[3]); using rit = std::reverse_iterator; - for(rit p = rit(data.plaintext.begin() + index + 3), - c = rit(data.ciphertext.begin() + data.offset + index + 3); - p != data.plaintext.rend(); - ++p, ++c) + for (rit p = rit(data.plaintext.begin() + index + 3), c = rit(data.ciphertext.begin() + data.offset + index + 3); + p != data.plaintext.rend(); ++p, ++c) { keysBackward.updateBackward(*c); - if((*c ^ keysBackward.getK()) != *p) + if ((*c ^ keysBackward.getK()) != *p) return; } std::size_t indexBackward = data.offset; // continue filtering with extra known plaintext - for(const std::pair& extra : data.extraPlaintext) + for (const std::pair& extra : data.extraPlaintext) { byte p; - if(extra.first < indexBackward) + if (extra.first < indexBackward) { keysBackward.updateBackward(data.ciphertext, indexBackward, extra.first); indexBackward = extra.first; - p = data.ciphertext[indexBackward] ^ keysBackward.getK(); + p = data.ciphertext[indexBackward] ^ keysBackward.getK(); } else { keysForward.update(data.ciphertext, indexForward, extra.first); indexForward = extra.first; - p = data.ciphertext[indexForward] ^ keysForward.getK(); + p = data.ciphertext[indexForward] ^ keysForward.getK(); } - if(p != extra.second) + if (p != extra.second) return; } @@ -168,43 +172,42 @@ void Attack::testXlist() solutions.push_back(keysBackward); } - progress.log([&keysBackward](std::ostream& os) - { - os << "Keys: " << keysBackward << std::endl; - }); + progress.log([&keysBackward](std::ostream& os) { os << "Keys: " << keysBackward << std::endl; }); - if(!exhaustive) + if (!exhaustive) progress.state = Progress::State::EarlyExit; } -std::vector attack(const Data& data, const u32vec& zi_2_32_vector, int& start, std::size_t index, int jobs, const bool exhaustive, Progress& progress) +std::vector attack(const Data& data, const u32vec& zi_2_32_vector, int& start, std::size_t index, int jobs, + const bool exhaustive, Progress& progress) { const uint32* candidates = zi_2_32_vector.data(); - const auto size = static_cast(zi_2_32_vector.size()); + const auto size = static_cast(zi_2_32_vector.size()); std::vector solutions; - std::mutex solutionsMutex; - Attack worker(data, index, solutions, solutionsMutex, exhaustive, progress); + std::mutex solutionsMutex; + Attack worker(data, index, solutions, solutionsMutex, exhaustive, progress); - progress.done = start; + progress.done = start; progress.total = size; - const auto threadCount = std::clamp(jobs, 1, size); - auto threads = std::vector{}; - auto nextCandidateIndex = std::atomic{start}; - for(auto i = 0; i < threadCount; ++i) + const auto threadCount = std::clamp(jobs, 1, size); + auto threads = std::vector{}; + auto nextCandidateIndex = std::atomic{start}; + for (auto i = 0; i < threadCount; ++i) threads.emplace_back( - [&nextCandidateIndex, size, &progress, candidates, worker]() mutable { - for(auto i = nextCandidateIndex++; i < size; i = nextCandidateIndex++) + [&nextCandidateIndex, size, &progress, candidates, worker]() mutable + { + for (auto i = nextCandidateIndex++; i < size; i = nextCandidateIndex++) { worker.carryout(candidates[i]); progress.done++; - if(progress.state != Progress::State::Normal) + if (progress.state != Progress::State::Normal) break; } }); - for(auto& thread : threads) + for (auto& thread : threads) thread.join(); start = nextCandidateIndex; diff --git a/src/ConsoleProgress.cpp b/src/ConsoleProgress.cpp index 23018e1..a816d53 100644 --- a/src/ConsoleProgress.cpp +++ b/src/ConsoleProgress.cpp @@ -1,9 +1,14 @@ #include "ConsoleProgress.hpp" + #include "log.hpp" ConsoleProgress::ConsoleProgress(std::ostream& os, const std::chrono::milliseconds& interval) -: Progress{os}, m_interval{interval}, m_in_destructor{false}, m_printer{&ConsoleProgress::printerFunction, this} -{} +: Progress{os} +, m_interval{interval} +, m_in_destructor{false} +, m_printer{&ConsoleProgress::printerFunction, this} +{ +} ConsoleProgress::~ConsoleProgress() { @@ -24,36 +29,39 @@ void ConsoleProgress::printerFunction() // the running operation is likely to have initialized the total number of steps. { std::unique_lock lock(m_in_destructor_mutex); - repeat = !m_in_destructor_cv.wait_for(lock, std::chrono::milliseconds(1), [this]{ return m_in_destructor; }); + repeat = !m_in_destructor_cv.wait_for(lock, std::chrono::milliseconds(1), [this] { return m_in_destructor; }); } - while(repeat) + while (repeat) { - if(int total = this->total.load()) - log([done = done.load(), total](std::ostream& os) - { - const auto flagsBefore = os.setf(std::ios::fixed, std::ios::floatfield); - const auto precisionBefore = os.precision(1); + if (int total = this->total.load()) + log( + [done = done.load(), total](std::ostream& os) + { + const auto flagsBefore = os.setf(std::ios::fixed, std::ios::floatfield); + const auto precisionBefore = os.precision(1); - os << (100.0 * done / total) << " % (" << done << " / " << total << ")" << std::flush << "\033[1K\r"; + os << (100.0 * done / total) << " % (" << done << " / " << total << ")" << std::flush + << "\033[1K\r"; - os.precision(precisionBefore); - os.flags(flagsBefore); - }); + os.precision(precisionBefore); + os.flags(flagsBefore); + }); std::unique_lock lock(m_in_destructor_mutex); - repeat = !m_in_destructor_cv.wait_for(lock, m_interval, [this]{ return m_in_destructor; }); + repeat = !m_in_destructor_cv.wait_for(lock, m_interval, [this] { return m_in_destructor; }); } - if(int total = this->total.load()) - log([done = done.load(), total](std::ostream& os) - { - const auto flagsBefore = os.setf(std::ios::fixed, std::ios::floatfield); - const auto precisionBefore = os.precision(1); + if (int total = this->total.load()) + log( + [done = done.load(), total](std::ostream& os) + { + const auto flagsBefore = os.setf(std::ios::fixed, std::ios::floatfield); + const auto precisionBefore = os.precision(1); - os << (100.0 * done / total) << " % (" << done << " / " << total << ")" << std::endl; + os << (100.0 * done / total) << " % (" << done << " / " << total << ")" << std::endl; - os.precision(precisionBefore); - os.flags(flagsBefore); - }); + os.precision(precisionBefore); + os.flags(flagsBefore); + }); } diff --git a/src/Crc32Tab.cpp b/src/Crc32Tab.cpp index 2fd9b3a..dc811f0 100644 --- a/src/Crc32Tab.cpp +++ b/src/Crc32Tab.cpp @@ -4,18 +4,18 @@ const Crc32Tab Crc32Tab::instance; Crc32Tab::Crc32Tab() { - for(int b = 0; b < 256; b++) + for (int b = 0; b < 256; b++) { uint32 crc = b; // compute CRC32 from the original definition - for(int i = 0; i < 8; i++) - if(crc & 1) + for (int i = 0; i < 8; i++) + if (crc & 1) crc = crc >> 1 ^ CRCPOL; else crc = crc >> 1; // fill lookup tables - crctab[b] = crc; + crctab[b] = crc; crcinvtab[msb(crc)] = crc << 8 ^ b; } } diff --git a/src/Data.cpp b/src/Data.cpp index 194c298..7575a49 100644 --- a/src/Data.cpp +++ b/src/Data.cpp @@ -1,5 +1,7 @@ #include "Data.hpp" + #include "Attack.hpp" + #include #include #include @@ -25,39 +27,41 @@ struct Range } // namespace Data::Error::Error(const std::string& description) - : BaseError("Data error", description) -{} +: BaseError("Data error", description) +{ +} Data::Data(bytevec ciphertextArg, bytevec plaintextArg, int offsetArg, const std::map& extraPlaintextArg) -: ciphertext(std::move(ciphertextArg)), plaintext(std::move(plaintextArg)) +: ciphertext(std::move(ciphertextArg)) +, plaintext(std::move(plaintextArg)) { // validate lengths - if(ciphertext.size() < Attack::ATTACK_SIZE) - throw Error("ciphertext is too small for an attack (minimum length is "+std::to_string(Attack::ATTACK_SIZE)+")"); - if(ciphertext.size() < plaintext.size()) + if (ciphertext.size() < Attack::ATTACK_SIZE) + throw Error("ciphertext is too small for an attack (minimum length is " + std::to_string(Attack::ATTACK_SIZE) + + ")"); + if (ciphertext.size() < plaintext.size()) throw Error("ciphertext is smaller than plaintext"); // validate offsets constexpr int minimumOffset = -static_cast(ENCRYPTION_HEADER_SIZE); - if(offsetArg < minimumOffset) - throw Error("plaintext offset "+std::to_string(offsetArg)+" is too small (minimum is "+std::to_string(minimumOffset)+")"); - if(ciphertext.size() < ENCRYPTION_HEADER_SIZE + offsetArg + plaintext.size()) - throw Error("plaintext offset "+std::to_string(offsetArg)+" is too large"); - - if(!extraPlaintextArg.empty() && extraPlaintextArg.begin()->first < minimumOffset) - throw Error("extra plaintext offset "+std::to_string(extraPlaintextArg.begin()->first)+" is too small (minimum is "+std::to_string(minimumOffset)+")"); - if(!extraPlaintextArg.empty() && ciphertext.size() <= ENCRYPTION_HEADER_SIZE + extraPlaintextArg.rbegin()->first) - throw Error("extra plaintext offset "+std::to_string(extraPlaintextArg.rbegin()->first)+" is too large"); + if (offsetArg < minimumOffset) + throw Error("plaintext offset " + std::to_string(offsetArg) + " is too small (minimum is " + + std::to_string(minimumOffset) + ")"); + if (ciphertext.size() < ENCRYPTION_HEADER_SIZE + offsetArg + plaintext.size()) + throw Error("plaintext offset " + std::to_string(offsetArg) + " is too large"); + + if (!extraPlaintextArg.empty() && extraPlaintextArg.begin()->first < minimumOffset) + throw Error("extra plaintext offset " + std::to_string(extraPlaintextArg.begin()->first) + + " is too small (minimum is " + std::to_string(minimumOffset) + ")"); + if (!extraPlaintextArg.empty() && ciphertext.size() <= ENCRYPTION_HEADER_SIZE + extraPlaintextArg.rbegin()->first) + throw Error("extra plaintext offset " + std::to_string(extraPlaintextArg.rbegin()->first) + " is too large"); // shift offsets to absolute values offset = ENCRYPTION_HEADER_SIZE + offsetArg; - std::transform(extraPlaintextArg.begin(), extraPlaintextArg.end(), - std::back_inserter(extraPlaintext), - [](const std::pair& extra) - { - return std::make_pair(ENCRYPTION_HEADER_SIZE + extra.first, extra.second); - }); + std::transform(extraPlaintextArg.begin(), extraPlaintextArg.end(), std::back_inserter(extraPlaintext), + [](const std::pair& extra) + { return std::make_pair(ENCRYPTION_HEADER_SIZE + extra.first, extra.second); }); // merge contiguous plaintext with adjacent extra plaintext { @@ -67,24 +71,21 @@ Data::Data(bytevec ciphertextArg, bytevec plaintextArg, int offsetArg, const std // - [after, extraPlaintext.end()) after contiguous plaintext auto before = std::lower_bound(extraPlaintext.begin(), extraPlaintext.end(), std::make_pair(offset, byte())); - auto after = std::lower_bound(before, extraPlaintext.end(), std::make_pair(offset + plaintext.size(), byte())); + auto after = std::lower_bound(before, extraPlaintext.end(), std::make_pair(offset + plaintext.size(), byte())); // overwrite overlapping plaintext std::for_each(before, after, - [this](const std::pair& e) - { - plaintext[e.first - offset] = e.second; - }); + [this](const std::pair& e) { plaintext[e.first - offset] = e.second; }); // merge contiguous plaintext with extra plaintext immediately before - while(before != extraPlaintext.begin() && (before - 1)->first == offset - 1) + while (before != extraPlaintext.begin() && (before - 1)->first == offset - 1) { plaintext.insert(plaintext.begin(), (--before)->second); offset--; } // merge contiguous plaintext with extra plaintext immediately after - while(after != extraPlaintext.end() && after->first == offset + plaintext.size()) + while (after != extraPlaintext.end() && after->first == offset + plaintext.size()) plaintext.push_back((after++)->second); // discard merged extra plaintext @@ -95,40 +96,41 @@ Data::Data(bytevec ciphertextArg, bytevec plaintextArg, int offsetArg, const std { Range range = {extraPlaintext.begin(), extraPlaintext.begin()}; // empty - for(auto it = extraPlaintext.begin(); it != extraPlaintext.end();) + for (auto it = extraPlaintext.begin(); it != extraPlaintext.end();) { Range current = {it, ++it}; - while(it != extraPlaintext.end() && it->first == (current.end - 1)->first + 1) + while (it != extraPlaintext.end() && it->first == (current.end - 1)->first + 1) current.end = ++it; range = std::max(range, current); } - if(plaintext.size() < range.size()) + if (plaintext.size() < range.size()) { const std::size_t plaintextSize = plaintext.size(); - const std::size_t rangeOffset = range.begin->first; + const std::size_t rangeOffset = range.begin->first; // append last bytes from the range to contiguous plaintext - for(std::size_t i = plaintextSize; i < range.size(); i++) + for (std::size_t i = plaintextSize; i < range.size(); i++) plaintext.push_back(range.begin[i].second); // remove those bytes from the range range.end = extraPlaintext.erase(range.begin + plaintextSize, range.end); - if(plaintextSize == 0) + if (plaintextSize == 0) range.begin = range.end; // rotate extra plaintext so that it will be sorted at the end of this scope { - auto before = std::lower_bound(extraPlaintext.begin(), extraPlaintext.end(), std::make_pair(offset, byte())); - if(offset < rangeOffset) + auto before = + std::lower_bound(extraPlaintext.begin(), extraPlaintext.end(), std::make_pair(offset, byte())); + if (offset < rangeOffset) range = {before, std::rotate(before, range.begin, range.end)}; else range = {std::rotate(range.begin, range.end, before), before}; } // swap bytes between the former contiguous plaintext and the beginning of the range - for(std::size_t i = 0; i < plaintextSize; i++) + for (std::size_t i = 0; i < plaintextSize; i++) { range.begin[i].first = offset + i; std::swap(plaintext[i], range.begin[i].second); @@ -139,24 +141,25 @@ Data::Data(bytevec ciphertextArg, bytevec plaintextArg, int offsetArg, const std } // check that there is enough known plaintext - if(plaintext.size() < Attack::CONTIGUOUS_SIZE) - throw Error("not enough contiguous plaintext ("+std::to_string(plaintext.size())+" bytes available, minimum is "+std::to_string(Attack::CONTIGUOUS_SIZE)+")"); - if(plaintext.size() + extraPlaintext.size() < Attack::ATTACK_SIZE) - throw Error("not enough plaintext ("+std::to_string(plaintext.size() + extraPlaintext.size())+" bytes available, minimum is "+std::to_string(Attack::ATTACK_SIZE)+")"); + if (plaintext.size() < Attack::CONTIGUOUS_SIZE) + throw Error("not enough contiguous plaintext (" + std::to_string(plaintext.size()) + + " bytes available, minimum is " + std::to_string(Attack::CONTIGUOUS_SIZE) + ")"); + if (plaintext.size() + extraPlaintext.size() < Attack::ATTACK_SIZE) + throw Error("not enough plaintext (" + std::to_string(plaintext.size() + extraPlaintext.size()) + + " bytes available, minimum is " + std::to_string(Attack::ATTACK_SIZE) + ")"); // reorder remaining extra plaintext for filtering { auto before = std::lower_bound(extraPlaintext.begin(), extraPlaintext.end(), std::make_pair(offset, byte())); std::reverse(extraPlaintext.begin(), before); std::inplace_merge(extraPlaintext.begin(), before, extraPlaintext.end(), - [this](const std::pair& a, const std::pair& b) - { - return absdiff(a.first, offset + Attack::CONTIGUOUS_SIZE) < absdiff(b.first, offset + Attack::CONTIGUOUS_SIZE); - }); + [this](const std::pair& a, const std::pair& b) { + return absdiff(a.first, offset + Attack::CONTIGUOUS_SIZE) < + absdiff(b.first, offset + Attack::CONTIGUOUS_SIZE); + }); } // compute keystream - std::transform(plaintext.begin(), plaintext.end(), - ciphertext.begin() + offset, - std::back_inserter(keystream), std::bit_xor()); + std::transform(plaintext.begin(), plaintext.end(), ciphertext.begin() + offset, std::back_inserter(keystream), + std::bit_xor()); } diff --git a/src/Keys.cpp b/src/Keys.cpp index edd3173..c65c15c 100644 --- a/src/Keys.cpp +++ b/src/Keys.cpp @@ -1,19 +1,22 @@ #include "Keys.hpp" Keys::Keys(uint32 x, uint32 y, uint32 z) - : x(x), y(y), z(z) -{} +: x(x) +, y(y) +, z(z) +{ +} Keys::Keys(const std::string& password) - : Keys() +: Keys() { - for(char p : password) + for (char p : password) update(p); } void Keys::update(const bytevec& ciphertext, std::size_t current, std::size_t target) { - for(bytevec::const_iterator i = ciphertext.begin() + current; i != ciphertext.begin() + target; ++i) + for (bytevec::const_iterator i = ciphertext.begin() + current; i != ciphertext.begin() + target; ++i) update(*i ^ getK()); } @@ -21,6 +24,6 @@ void Keys::updateBackward(const bytevec& ciphertext, std::size_t current, std::s { using rit = std::reverse_iterator; - for(rit i = rit(ciphertext.begin() + current); i != rit(ciphertext.begin() + target); ++i) + for (rit i = rit(ciphertext.begin() + current); i != rit(ciphertext.begin() + target); ++i) updateBackward(*i); } diff --git a/src/KeystreamTab.cpp b/src/KeystreamTab.cpp index cc2fec9..0efe078 100644 --- a/src/KeystreamTab.cpp +++ b/src/KeystreamTab.cpp @@ -4,9 +4,9 @@ const KeystreamTab KeystreamTab::instance; KeystreamTab::KeystreamTab() { - for(uint32 z_2_16 = 0; z_2_16 < 1 << 16; z_2_16 += 4) + for (uint32 z_2_16 = 0; z_2_16 < 1 << 16; z_2_16 += 4) { - byte k = lsb((z_2_16 | 2) * (z_2_16 | 3) >> 8); + byte k = lsb((z_2_16 | 2) * (z_2_16 | 3) >> 8); keystreamtab[z_2_16 >> 2] = k; keystreaminvfiltertab[k][z_2_16 >> 10].push_back(z_2_16); keystreaminvexists[k].set(z_2_16 >> 10); diff --git a/src/MultTab.cpp b/src/MultTab.cpp index 536bc62..0f6d8f6 100644 --- a/src/MultTab.cpp +++ b/src/MultTab.cpp @@ -4,18 +4,18 @@ const MultTab MultTab::instance; MultTab::MultTab() { - uint32 prod = 0; // x * MULT + uint32 prod = 0; // x * MULT uint32 prodinv = 0; // x * MULT^-1 - for(int x = 0; x < 256; x++, prod += MULT, prodinv += MULTINV) + for (int x = 0; x < 256; x++, prod += MULT, prodinv += MULTINV) { - multtab[x] = prod; + multtab[x] = prod; multinvtab[x] = prodinv; msbprodfiber2[msb(prodinv)].push_back(x); msbprodfiber2[(msb(prodinv) + 1) % 256].push_back(x); msbprodfiber3[(msb(prodinv) + 255) % 256].push_back(x); - msbprodfiber3[ msb(prodinv) ].push_back(x); - msbprodfiber3[(msb(prodinv) + 1) % 256].push_back(x); + msbprodfiber3[msb(prodinv)].push_back(x); + msbprodfiber3[(msb(prodinv) + 1) % 256].push_back(x); } } diff --git a/src/Progress.cpp b/src/Progress.cpp index a652941..f2df20f 100644 --- a/src/Progress.cpp +++ b/src/Progress.cpp @@ -2,4 +2,5 @@ Progress::Progress(std::ostream& os) : m_os{os} -{} +{ +} diff --git a/src/SigintHandler.cpp b/src/SigintHandler.cpp index 28e8f0b..b8a3214 100644 --- a/src/SigintHandler.cpp +++ b/src/SigintHandler.cpp @@ -1,4 +1,5 @@ #include "SigintHandler.hpp" + #include namespace diff --git a/src/VirtualTerminalSupport.cpp b/src/VirtualTerminalSupport.cpp index 03ec1f3..49400ac 100644 --- a/src/VirtualTerminalSupport.cpp +++ b/src/VirtualTerminalSupport.cpp @@ -2,8 +2,8 @@ #ifdef _WIN32 -#include #include +#include class VirtualTerminalSupport::Impl { @@ -11,34 +11,37 @@ class VirtualTerminalSupport::Impl Impl() : hStdOut{GetStdHandle(STD_OUTPUT_HANDLE)} , originalMode{[this] - { - DWORD mode; - return GetConsoleMode(hStdOut, &mode) ? std::optional{mode} : std::nullopt; - }()} + { + DWORD mode; + return GetConsoleMode(hStdOut, &mode) ? std::optional{mode} : std::nullopt; + }()} { - if(originalMode) + if (originalMode) SetConsoleMode(hStdOut, *originalMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); } ~Impl() { - if(originalMode) + if (originalMode) SetConsoleMode(hStdOut, *originalMode); } private: - const HANDLE hStdOut; + const HANDLE hStdOut; const std::optional originalMode; }; #else -class VirtualTerminalSupport::Impl {}; +class VirtualTerminalSupport::Impl +{ +}; #endif // _WIN32 VirtualTerminalSupport::VirtualTerminalSupport() : m_impl{std::make_unique()} -{} +{ +} VirtualTerminalSupport::~VirtualTerminalSupport() = default; diff --git a/src/Zip.cpp b/src/Zip.cpp index 610df24..0aa6fc7 100644 --- a/src/Zip.cpp +++ b/src/Zip.cpp @@ -1,8 +1,10 @@ #include "Zip.hpp" + #include "file.hpp" + #include -#include #include +#include namespace { @@ -14,7 +16,7 @@ std::istream& read(std::istream& is, T& x) // We make no assumption about platform endianness. x = T(); - for(std::size_t index = 0; index < N; index++) + for (std::size_t index = 0; index < N; index++) x |= static_cast(is.get()) << (8 * index); return is; @@ -32,7 +34,7 @@ std::ostream& write(std::ostream& os, const T& x) static_assert(N <= sizeof(T), "write requires input type to have at least N bytes"); // We make no assumption about platform endianness. - for(std::size_t index = 0; index < N; index++) + for (std::size_t index = 0; index < N; index++) os.put(lsb(x >> (8 * index))); return os; @@ -40,11 +42,11 @@ std::ostream& write(std::ostream& os, const T& x) enum class Signature : uint32 { - LOCAL_FILE_HEADER = 0x04034b50, + LOCAL_FILE_HEADER = 0x04034b50, CENTRAL_DIRECTORY_HEADER = 0x02014b50, - ZIP64_EOCD = 0x06064b50, - ZIP64_EOCD_LOCATOR = 0x07064b50, - EOCD = 0x06054b50 + ZIP64_EOCD = 0x06064b50, + ZIP64_EOCD_LOCATOR = 0x07064b50, + EOCD = 0x06054b50 }; bool checkSignature(std::istream& is, const Signature& signature) @@ -64,9 +66,10 @@ uint64 findCentralDirectoryOffset(std::istream& is) do { is.seekg(-22 - commentLength, std::ios::end); - } while(read(is, signature) && signature != static_cast(Signature::EOCD) && commentLength++ < MASK_0_16); + } while (read(is, signature) && signature != static_cast(Signature::EOCD) && + commentLength++ < MASK_0_16); - if(!is || signature != static_cast(Signature::EOCD)) + if (!is || signature != static_cast(Signature::EOCD)) throw Zip::Error("could not find end of central directory signature"); } @@ -78,27 +81,27 @@ uint64 findCentralDirectoryOffset(std::istream& is) is.seekg(10, std::ios::cur); read(is, centralDirectoryOffset); - if(!is) + if (!is) throw Zip::Error("could not read end of central directory record"); - if(disk != 0) + if (disk != 0) throw Zip::Error("split zip archives are not supported"); } // look for Zip64 end of central directory locator is.seekg(-40, std::ios::cur); - if(checkSignature(is, Signature::ZIP64_EOCD_LOCATOR)) + if (checkSignature(is, Signature::ZIP64_EOCD_LOCATOR)) { uint64 zip64EndOfCentralDirectoryOffset; is.seekg(4, std::ios::cur); read(is, zip64EndOfCentralDirectoryOffset); - if(!is) + if (!is) throw Zip::Error("could not read Zip64 end of central directory locator record"); // read Zip64 end of central directory record is.seekg(zip64EndOfCentralDirectoryOffset, std::ios::beg); - if(checkSignature(is, Signature::ZIP64_EOCD)) + if (checkSignature(is, Signature::ZIP64_EOCD)) { uint16 versionNeededToExtract; @@ -107,9 +110,9 @@ uint64 findCentralDirectoryOffset(std::istream& is) is.seekg(32, std::ios::cur); read(is, centralDirectoryOffset); - if(!is) + if (!is) throw Zip::Error("could not read Zip64 end of central directory record"); - if(versionNeededToExtract >= 62) // Version 6.2 introduces central directory encryption. + if (versionNeededToExtract >= 62) // Version 6.2 introduces central directory encryption. throw Zip::Error("central directory encryption is not supported"); } else @@ -123,7 +126,8 @@ uint64 findCentralDirectoryOffset(std::istream& is) Zip::Error::Error(const std::string& description) : BaseError("Zip error", description) -{} +{ +} Zip::Iterator::Iterator(const Zip& archive) : m_is{&archive.m_is.seekg(archive.m_centralDirectoryOffset, std::ios::beg)} @@ -134,7 +138,7 @@ Zip::Iterator::Iterator(const Zip& archive) Zip::Iterator& Zip::Iterator::operator++() { - if(!checkSignature(*m_is, Signature::CENTRAL_DIRECTORY_HEADER)) + if (!checkSignature(*m_is, Signature::CENTRAL_DIRECTORY_HEADER)) return *this = Iterator{}; uint16 flags; @@ -161,21 +165,15 @@ Zip::Iterator& Zip::Iterator::operator++() read(*m_is, m_entry->offset); read(*m_is, m_entry->name, filenameLength); - m_entry->encryption = - flags & 1 - ? method == 99 || (flags >> 6) & 1 - ? Encryption::Unsupported - : Encryption::Traditional - : Encryption::None; + m_entry->encryption = flags & 1 + ? method == 99 || (flags >> 6) & 1 ? Encryption::Unsupported : Encryption::Traditional + : Encryption::None; m_entry->compression = static_cast(method); - m_entry->checkByte = - (flags >> 3) & 1 - ? static_cast(lastModTime >> 8) - : msb(m_entry->crc32); + m_entry->checkByte = (flags >> 3) & 1 ? static_cast(lastModTime >> 8) : msb(m_entry->crc32); - for(int remaining = extraFieldLength; remaining > 0; ) + for (int remaining = extraFieldLength; remaining > 0;) { // read extra field header uint16 id; @@ -184,61 +182,61 @@ Zip::Iterator& Zip::Iterator::operator++() read(*m_is, size); remaining -= 4 + size; - switch(id) + switch (id) { - case 0x0001: // Zip64 extended information - if(8 <= size && m_entry->uncompressedSize == MASK_0_32) - { - read(*m_is, m_entry->uncompressedSize); - size -= 8; - } - if(8 <= size && m_entry->packedSize == MASK_0_32) - { - read(*m_is, m_entry->packedSize); - size -= 8; - } - if(8 <= size && m_entry->offset == MASK_0_32) - { - read(*m_is, m_entry->offset); - size -= 8; - } - break; + case 0x0001: // Zip64 extended information + if (8 <= size && m_entry->uncompressedSize == MASK_0_32) + { + read(*m_is, m_entry->uncompressedSize); + size -= 8; + } + if (8 <= size && m_entry->packedSize == MASK_0_32) + { + read(*m_is, m_entry->packedSize); + size -= 8; + } + if (8 <= size && m_entry->offset == MASK_0_32) + { + read(*m_is, m_entry->offset); + size -= 8; + } + break; + + case 0x7075: // Info-ZIP Unicode Path + if (5 <= size) + { + uint32 nameCrc32 = MASK_0_32; + for (byte b : m_entry->name) + nameCrc32 = Crc32Tab::crc32(nameCrc32, b); + nameCrc32 ^= MASK_0_32; + + uint32 expectedNameCrc32; + m_is->seekg(1, std::ios::cur); + read(*m_is, expectedNameCrc32); + size -= 5; - case 0x7075: // Info-ZIP Unicode Path - if(5 <= size) + if (nameCrc32 == expectedNameCrc32) { - uint32 nameCrc32 = MASK_0_32; - for (byte b : m_entry->name) - nameCrc32 = Crc32Tab::crc32(nameCrc32, b); - nameCrc32 ^= MASK_0_32; - - uint32 expectedNameCrc32; - m_is->seekg(1, std::ios::cur); - read(*m_is, expectedNameCrc32); - size -= 5; - - if (nameCrc32 == expectedNameCrc32) - { - read(*m_is, m_entry->name, size); - size = 0; - } + read(*m_is, m_entry->name, size); + size = 0; } - break; + } + break; - case 0x9901: // AE-x encryption structure - if(7 <= size) - { - uint16 method; - m_is->seekg(5, std::ios::cur); - read(*m_is, method); - size -= 7; + case 0x9901: // AE-x encryption structure + if (7 <= size) + { + uint16 method; + m_is->seekg(5, std::ios::cur); + read(*m_is, method); + size -= 7; - m_entry->compression = static_cast(method); - } - break; + m_entry->compression = static_cast(method); + } + break; - default: - break; + default: + break; } // jump to the end of this data block @@ -247,7 +245,7 @@ Zip::Iterator& Zip::Iterator::operator++() m_is->seekg(fileCommentLength, std::ios::cur); - if(!*m_is) + if (!*m_is) throw Error("could not read central directory header"); return *this; @@ -264,24 +262,22 @@ Zip::Zip(std::istream& stream) : m_file{} , m_is{stream} , m_centralDirectoryOffset{findCentralDirectoryOffset(m_is)} -{} +{ +} Zip::Zip(const std::string& filename) : m_file{openInput(filename)} , m_is{*m_file} , m_centralDirectoryOffset{findCentralDirectoryOffset(m_is)} -{} +{ +} Zip::Entry Zip::operator[](const std::string& name) const { - const auto it = std::find_if(begin(), end(), - [&name](const Entry& entry) - { - return entry.name == name; - }); + const auto it = std::find_if(begin(), end(), [&name](const Entry& entry) { return entry.name == name; }); - if(it == end()) - throw Error("found no entry named \""+name+"\""); + if (it == end()) + throw Error("found no entry named \"" + name + "\""); else return *it; } @@ -289,35 +285,32 @@ Zip::Entry Zip::operator[](const std::string& name) const Zip::Entry Zip::operator[](std::size_t index) const { std::size_t nextIndex = 0; - const auto it = std::find_if(begin(), end(), - [&nextIndex, index](const Entry&) - { - return nextIndex++ == index; - }); + const auto it = std::find_if(begin(), end(), [&nextIndex, index](const Entry&) { return nextIndex++ == index; }); - if(it == end()) - throw Error("found no entry at index "+std::to_string(index)+" (maximum index for this archive is "+std::to_string(nextIndex - 1)+")"); + if (it == end()) + throw Error("found no entry at index " + std::to_string(index) + " (maximum index for this archive is " + + std::to_string(nextIndex - 1) + ")"); else return *it; } void Zip::checkEncryption(const Entry& entry, Encryption expected) { - if(entry.encryption != expected) + if (entry.encryption != expected) { - if(entry.encryption == Encryption::None) - throw Error("entry \""+entry.name+"\" is not encrypted"); - else if(expected == Encryption::None) - throw Error("entry \""+entry.name+"\" is encrypted"); + if (entry.encryption == Encryption::None) + throw Error("entry \"" + entry.name + "\" is not encrypted"); + else if (expected == Encryption::None) + throw Error("entry \"" + entry.name + "\" is encrypted"); else - throw Error("entry \""+entry.name+"\" is encrypted with an unsupported algorithm"); + throw Error("entry \"" + entry.name + "\" is encrypted with an unsupported algorithm"); } } std::istream& Zip::seek(const Entry& entry) const { m_is.seekg(entry.offset, std::ios::beg); - if(!checkSignature(m_is, Signature::LOCAL_FILE_HEADER)) + if (!checkSignature(m_is, Signature::LOCAL_FILE_HEADER)) throw Error("could not find local file header"); // skip local file header @@ -340,26 +333,27 @@ void Zip::changeKeys(std::ostream& os, const Keys& oldKeys, const Keys& newKeys, // Store encrypted entries local file header offset and packed size. // Use std::map to sort them by local file header offset. std::map packedSizeByLocalOffset; - for(const auto& entry : *this) - if(entry.encryption == Encryption::Traditional) + for (const auto& entry : *this) + if (entry.encryption == Encryption::Traditional) packedSizeByLocalOffset.insert({entry.offset, entry.packedSize}); // Rewind input stream and iterate on encrypted entries to change the keys, copy the rest. m_is.seekg(0, std::ios::beg); uint64 currentOffset = 0; - progress.done = 0; + progress.done = 0; progress.total = packedSizeByLocalOffset.size(); - for(const auto& [localHeaderOffset, packedSize] : packedSizeByLocalOffset) + for (const auto& [localHeaderOffset, packedSize] : packedSizeByLocalOffset) { - if(currentOffset < localHeaderOffset) + if (currentOffset < localHeaderOffset) { - std::copy_n(std::istreambuf_iterator(m_is), localHeaderOffset - currentOffset, std::ostreambuf_iterator(os)); + std::copy_n(std::istreambuf_iterator(m_is), localHeaderOffset - currentOffset, + std::ostreambuf_iterator(os)); m_is.get(); } - if(!checkSignature(m_is, Signature::LOCAL_FILE_HEADER)) + if (!checkSignature(m_is, Signature::LOCAL_FILE_HEADER)) throw Error("could not find local file header"); write(os, static_cast(Signature::LOCAL_FILE_HEADER)); @@ -373,29 +367,31 @@ void Zip::changeKeys(std::ostream& os, const Keys& oldKeys, const Keys& newKeys, write(os, filenameLength); write(os, extraSize); - if(0 < filenameLength + extraSize) + if (0 < filenameLength + extraSize) { - std::copy_n(std::istreambuf_iterator(m_is), filenameLength + extraSize, std::ostreambuf_iterator(os)); + std::copy_n(std::istreambuf_iterator(m_is), filenameLength + extraSize, + std::ostreambuf_iterator(os)); m_is.get(); } - Keys decrypt = oldKeys, - encrypt = newKeys; + Keys decrypt = oldKeys; + Keys encrypt = newKeys; std::istreambuf_iterator in(m_is); std::generate_n(std::ostreambuf_iterator(os), packedSize, - [&in, &decrypt, &encrypt]() -> char - { - byte p = *in++ ^ decrypt.getK(); - byte c = p ^ encrypt.getK(); - decrypt.update(p); - encrypt.update(p); - return c; - }); + [&in, &decrypt, &encrypt]() -> char + { + byte p = *in++ ^ decrypt.getK(); + byte c = p ^ encrypt.getK(); + decrypt.update(p); + encrypt.update(p); + return c; + }); currentOffset = localHeaderOffset + 30 + filenameLength + extraSize + packedSize; progress.done++; } - std::copy(std::istreambuf_iterator(m_is), std::istreambuf_iterator(), std::ostreambuf_iterator(os)); + std::copy(std::istreambuf_iterator(m_is), std::istreambuf_iterator(), + std::ostreambuf_iterator(os)); } diff --git a/src/Zreduction.cpp b/src/Zreduction.cpp index a6a9f55..cee857e 100644 --- a/src/Zreduction.cpp +++ b/src/Zreduction.cpp @@ -1,84 +1,86 @@ #include "Zreduction.hpp" + #include "Attack.hpp" #include "Crc32Tab.hpp" #include "KeystreamTab.hpp" + #include #include Zreduction::Zreduction(const bytevec& keystream) - : keystream(keystream) +: keystream(keystream) { index = keystream.size() - 1; - zi_vector.reserve(1<<22); + zi_vector.reserve(1 << 22); - for(uint32 zi_10_32_shifted = 0; zi_10_32_shifted < 1<<22; zi_10_32_shifted++) - if(KeystreamTab::hasZi_2_16(keystream[index], zi_10_32_shifted << 10)) + for (uint32 zi_10_32_shifted = 0; zi_10_32_shifted < 1 << 22; zi_10_32_shifted++) + if (KeystreamTab::hasZi_2_16(keystream[index], zi_10_32_shifted << 10)) zi_vector.push_back(zi_10_32_shifted << 10); } void Zreduction::reduce(Progress& progress) { // variables to keep track of the smallest Zi[2,32) vector - bool tracking = false; - u32vec bestCopy; + bool tracking = false; + u32vec bestCopy; std::size_t bestIndex = index, bestSize = TRACK_SIZE; // variables to wait for a limited number of steps when a small enough vector is found - bool waiting = false; - std::size_t wait = 0; + bool waiting = false; + std::size_t wait = 0; u32vec zim1_10_32_vector; - zim1_10_32_vector.reserve(1<<22); - std::bitset<1<<22> zim1_10_32_set; + zim1_10_32_vector.reserve(1 << 22); + std::bitset<1 << 22> zim1_10_32_set; - progress.done = 0; + progress.done = 0; progress.total = keystream.size() - Attack::CONTIGUOUS_SIZE; - for(std::size_t i = index; i >= Attack::CONTIGUOUS_SIZE; i--) + for (std::size_t i = index; i >= Attack::CONTIGUOUS_SIZE; i--) { zim1_10_32_vector.clear(); zim1_10_32_set.reset(); std::size_t number_of_zim1_2_32 = 0; // generate the Z{i-1}[10,32) values - for(uint32 zi_10_32 : zi_vector) - for(uint32 zi_2_16 : KeystreamTab::getZi_2_16_vector(keystream[i], zi_10_32)) + for (uint32 zi_10_32 : zi_vector) + for (uint32 zi_2_16 : KeystreamTab::getZi_2_16_vector(keystream[i], zi_10_32)) { // get Z{i-1}[10,32) from CRC32^-1 uint32 zim1_10_32 = Crc32Tab::getZim1_10_32(zi_10_32 | zi_2_16); // collect without duplicates only those that are compatible with keystream{i-1} - if(!zim1_10_32_set[zim1_10_32 >> 10] && KeystreamTab::hasZi_2_16(keystream[i-1], zim1_10_32)) + if (!zim1_10_32_set[zim1_10_32 >> 10] && KeystreamTab::hasZi_2_16(keystream[i - 1], zim1_10_32)) { zim1_10_32_vector.push_back(zim1_10_32); zim1_10_32_set.set(zim1_10_32 >> 10); - number_of_zim1_2_32 += KeystreamTab::getZi_2_16_vector(keystream[i-1], zim1_10_32).size(); + number_of_zim1_2_32 += KeystreamTab::getZi_2_16_vector(keystream[i - 1], zim1_10_32).size(); } } // update smallest vector tracking - if(number_of_zim1_2_32 <= bestSize) // new smallest number of Z[2,32) values + if (number_of_zim1_2_32 <= bestSize) // new smallest number of Z[2,32) values { - tracking = true; - bestIndex = i-1; - bestSize = number_of_zim1_2_32; - waiting = false; + tracking = true; + bestIndex = i - 1; + bestSize = number_of_zim1_2_32; + waiting = false; } - else if(tracking) // number of Z{i-1}[2,32) values is bigger than bestSize + else if (tracking) // number of Z{i-1}[2,32) values is bigger than bestSize { - if(bestIndex == i) // hit a minimum + if (bestIndex == i) // hit a minimum { // keep a copy of the vector because size is about to grow std::swap(bestCopy, zi_vector); - if(bestSize <= WAIT_SIZE) + if (bestSize <= WAIT_SIZE) { // enable waiting waiting = true; - wait = bestSize * 4; // arbitrary multiplicative constant + wait = bestSize * 4; // arbitrary multiplicative constant } } - if(waiting && --wait == 0) + if (waiting && --wait == 0) break; } @@ -88,10 +90,10 @@ void Zreduction::reduce(Progress& progress) progress.done++; } - if(tracking) + if (tracking) { // put bestCopy in zi_vector only if bestIndex is not the index of zi_vector - if(bestIndex != Attack::CONTIGUOUS_SIZE - 1) + if (bestIndex != Attack::CONTIGUOUS_SIZE - 1) std::swap(zi_vector, bestCopy); index = bestIndex; } @@ -102,10 +104,10 @@ void Zreduction::reduce(Progress& progress) void Zreduction::generate() { std::size_t number_of_zi_10_32 = zi_vector.size(); - for(std::size_t i = 0; i < number_of_zi_10_32; i++) + for (std::size_t i = 0; i < number_of_zi_10_32; i++) { const u32vec& zi_2_16_vector = KeystreamTab::getZi_2_16_vector(keystream[index], zi_vector[i]); - for(std::size_t j = 1; j < zi_2_16_vector.size(); j++) + for (std::size_t j = 1; j < zi_2_16_vector.size(); j++) zi_vector.push_back(zi_vector[i] | zi_2_16_vector[j]); zi_vector[i] |= zi_2_16_vector[0]; } diff --git a/src/file.cpp b/src/file.cpp index 54e13f8..96e12b6 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -1,12 +1,13 @@ #include "file.hpp" FileError::FileError(const std::string& description) - : BaseError("File error", description) -{} +: BaseError("File error", description) +{ +} std::ifstream openInput(const std::string& filename) { - if(std::ifstream is = std::ifstream(filename, std::ios::binary)) + if (std::ifstream is = std::ifstream(filename, std::ios::binary)) return is; else throw FileError("could not open input file " + filename); @@ -14,9 +15,9 @@ std::ifstream openInput(const std::string& filename) bytevec loadStream(std::istream& is, std::size_t size) { - bytevec content; + bytevec content; std::istreambuf_iterator it(is); - for(std::size_t i = 0; i < size && it != std::istreambuf_iterator(); i++, ++it) + for (std::size_t i = 0; i < size && it != std::istreambuf_iterator(); i++, ++it) content.push_back(*it); return content; @@ -30,7 +31,7 @@ bytevec loadFile(const std::string& filename, std::size_t size) std::ofstream openOutput(const std::string& filename) { - if(std::ofstream os = std::ofstream(filename, std::ios::binary)) + if (std::ofstream os = std::ofstream(filename, std::ios::binary)) return os; else throw FileError("could not open output file " + filename); diff --git a/src/log.cpp b/src/log.cpp index f4877f2..d3aedec 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -1,5 +1,7 @@ #include "log.hpp" + #include "Keys.hpp" + #include #include @@ -12,11 +14,9 @@ std::ostream& put_time(std::ostream& os) std::ostream& operator<<(std::ostream& os, const Keys& keys) { const auto flagsBefore = os.setf(std::ios::hex, std::ios::basefield); - const auto fillBefore = os.fill('0'); + const auto fillBefore = os.fill('0'); - os << std::setw(8) << keys.getX() << " " - << std::setw(8) << keys.getY() << " " - << std::setw(8) << keys.getZ(); + os << std::setw(8) << keys.getX() << " " << std::setw(8) << keys.getY() << " " << std::setw(8) << keys.getZ(); os.fill(fillBefore); os.flags(flagsBefore); diff --git a/src/main.cpp b/src/main.cpp index 27ad970..d1ebff4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,15 +1,16 @@ -#include "VirtualTerminalSupport.hpp" -#include "log.hpp" +#include "Arguments.hpp" +#include "Attack.hpp" #include "ConsoleProgress.hpp" +#include "Data.hpp" #include "SigintHandler.hpp" -#include "file.hpp" +#include "VirtualTerminalSupport.hpp" #include "Zip.hpp" -#include "Arguments.hpp" -#include "Data.hpp" #include "Zreduction.hpp" -#include "Attack.hpp" +#include "file.hpp" +#include "log.hpp" #include "password.hpp" #include "version.hpp" + #include #include #include @@ -101,7 +102,7 @@ void decipher(std::istream& is, std::size_t size, std::size_t discard, std::ostr } // namespace -int main(int argc, char const *argv[]) +int main(int argc, const char* argv[]) try { // enable virtual terminal support on Windows, no-op on other platforms @@ -111,31 +112,32 @@ try std::cout << "bkcrack " BKCRACK_VERSION " - " BKCRACK_VERSION_DATE << std::endl; const Arguments args(argc, argv); - if(args.help) + if (args.help) { std::cout << usage << std::endl; return 0; } - if(args.version) + if (args.version) { // version information was already printed, nothing else to do return 0; } - if(args.infoArchive) + if (args.infoArchive) { listEntries(*args.infoArchive); return 0; } std::vector keysvec; - if(args.keys) + if (args.keys) keysvec.push_back(*args.keys); - else if(args.password) + else if (args.password) { keysvec.emplace_back(*args.password); - std::cout << "Internal representation for password \"" << *args.password << "\": " << keysvec.back() << std::endl; + std::cout << "Internal representation for password \"" << *args.password << "\": " << keysvec.back() + << std::endl; } else // find keys from known plaintext @@ -144,9 +146,10 @@ try // generate and reduce Zi[10,32) values Zreduction zr(data.keystream); - if(data.keystream.size() > Attack::CONTIGUOUS_SIZE) + if (data.keystream.size() > Attack::CONTIGUOUS_SIZE) { - std::cout << "[" << put_time << "] Z reduction using " << (data.keystream.size() - Attack::CONTIGUOUS_SIZE) << " bytes of known plaintext" << std::endl; + std::cout << "[" << put_time << "] Z reduction using " << (data.keystream.size() - Attack::CONTIGUOUS_SIZE) + << " bytes of known plaintext" << std::endl; ConsoleProgress progress(std::cout); zr.reduce(progress); @@ -157,22 +160,23 @@ try // carry out the attack on the remaining Zi[2,32) values std::cout << "[" << put_time << "] Attack on " << zr.getCandidates().size() << " Z values at index " - << (static_cast(data.offset + zr.getIndex()) - static_cast(Data::ENCRYPTION_HEADER_SIZE)) << std::endl; + << (static_cast(data.offset + zr.getIndex()) - static_cast(Data::ENCRYPTION_HEADER_SIZE)) + << std::endl; const auto [state, restart] = [&]() -> std::pair { - int start = args.attackStart; + int start = args.attackStart; ConsoleProgress progress(std::cout); - SigintHandler sigintHandler{progress.state}; + SigintHandler sigintHandler{progress.state}; keysvec = attack(data, zr.getCandidates(), start, zr.getIndex(), args.jobs, args.exhaustive, progress); return {progress.state, start}; }(); - if(state != Progress::State::Normal) + if (state != Progress::State::Normal) { - if(state == Progress::State::Canceled) + if (state == Progress::State::Canceled) std::cout << "Operation interrupted by user." << std::endl; - else if(state == Progress::State::EarlyExit) + else if (state == Progress::State::EarlyExit) std::cout << "Found a solution. Stopping." << std::endl; std::cout << "You may resume the attack with the option: --continue-attack " << restart << std::endl; @@ -180,7 +184,7 @@ try // print the keys std::cout << "[" << put_time << "] "; - if(keysvec.empty()) + if (keysvec.empty()) { std::cout << "Could not find the keys." << std::endl; return 1; @@ -188,7 +192,7 @@ try else { std::cout << "Keys" << std::endl; - for(const Keys& keys : keysvec) + for (const Keys& keys : keysvec) std::cout << keys << std::endl; } } @@ -196,26 +200,26 @@ try // From there, keysvec is not empty. const Keys keys = keysvec.front(); - if((args.decipheredFile || args.changePassword || args.changeKeys || args.bruteforce) && keysvec.size() > 1) + if ((args.decipheredFile || args.changePassword || args.changeKeys || args.bruteforce) && keysvec.size() > 1) std::cout << "Continuing with keys " << keys << "\n" << "Use the command line option -k to provide other keys." << std::endl; // decipher - if(args.decipheredFile) + if (args.decipheredFile) { std::cout << "[" << put_time << "] Writing deciphered data " << *args.decipheredFile << " (maybe compressed)"; - if(args.keepHeader) + if (args.keepHeader) std::cout << " with encryption header"; std::cout << std::endl; { std::ifstream cipherstream = openInput(args.cipherArchive ? *args.cipherArchive : *args.cipherFile); - std::size_t ciphersize = std::numeric_limits::max(); + std::size_t ciphersize = std::numeric_limits::max(); - if(args.cipherArchive) + if (args.cipherArchive) { const auto archive = Zip{cipherstream}; - const auto entry = args.cipherFile ? archive[*args.cipherFile] : archive[*args.cipherIndex]; + const auto entry = args.cipherFile ? archive[*args.cipherFile] : archive[*args.cipherIndex]; Zip::checkEncryption(entry, Zip::Encryption::Traditional); archive.seek(entry); @@ -224,21 +228,24 @@ try std::ofstream decipheredstream = openOutput(*args.decipheredFile); - decipher(cipherstream, ciphersize, args.keepHeader ? 0 : static_cast(Data::ENCRYPTION_HEADER_SIZE), decipheredstream, keys); + decipher(cipherstream, ciphersize, + args.keepHeader ? 0 : static_cast(Data::ENCRYPTION_HEADER_SIZE), decipheredstream, + keys); } std::cout << "Wrote deciphered data." << std::endl; } // unlock - if(args.changePassword) + if (args.changePassword) { const auto& [unlockedArchive, newPassword] = *args.changePassword; - std::cout << "[" << put_time << "] Writing unlocked archive " << unlockedArchive << " with password \"" << newPassword << "\"" << std::endl; + std::cout << "[" << put_time << "] Writing unlocked archive " << unlockedArchive << " with password \"" + << newPassword << "\"" << std::endl; { - const auto archive = Zip{*args.cipherArchive}; + const auto archive = Zip{*args.cipherArchive}; std::ofstream unlocked = openOutput(unlockedArchive); ConsoleProgress progress(std::cout); @@ -248,14 +255,15 @@ try std::cout << "Wrote unlocked archive." << std::endl; } - if(args.changeKeys) + if (args.changeKeys) { const auto& [unlockedArchive, newKeys] = *args.changeKeys; - std::cout << "[" << put_time << "] Writing unlocked archive " << unlockedArchive << " with keys " << newKeys << std::endl; + std::cout << "[" << put_time << "] Writing unlocked archive " << unlockedArchive << " with keys " << newKeys + << std::endl; { - const auto archive = Zip{*args.cipherArchive}; + const auto archive = Zip{*args.cipherArchive}; std::ofstream unlocked = openOutput(unlockedArchive); ConsoleProgress progress(std::cout); @@ -266,7 +274,7 @@ try } // recover password - if(args.bruteforce) + if (args.bruteforce) { std::cout << "[" << put_time << "] Recovering password" << std::endl; @@ -274,30 +282,31 @@ try const auto [state, restart] = [&]() -> std::pair { - const auto& charset = *args.bruteforce; + const auto& charset = *args.bruteforce; const auto& [minLength, maxLength] = args.length.value_or(Arguments::LengthInterval{}); - std::string start = args.recoveryStart; + std::string start = args.recoveryStart; ConsoleProgress progress(std::cout); - SigintHandler sigintHandler(progress.state); - passwords = recoverPassword(keysvec.front(), charset, minLength, maxLength, start, args.jobs, args.exhaustive, progress); + SigintHandler sigintHandler(progress.state); + passwords = recoverPassword(keysvec.front(), charset, minLength, maxLength, start, args.jobs, + args.exhaustive, progress); return {progress.state, start}; }(); - if(state != Progress::State::Normal) + if (state != Progress::State::Normal) { - if(state == Progress::State::Canceled) + if (state == Progress::State::Canceled) std::cout << "Operation interrupted by user." << std::endl; - else if(state == Progress::State::EarlyExit) + else if (state == Progress::State::EarlyExit) std::cout << "Found a solution. Stopping." << std::endl; - if(!restart.empty()) + if (!restart.empty()) { const auto flagsBefore = std::cout.setf(std::ios::hex, std::ios::basefield); - const auto fillBefore = std::cout.fill('0'); + const auto fillBefore = std::cout.fill('0'); std::cout << "You may resume the password recovery with the option: --continue-recovery "; - for(byte c : restart) + for (byte c : restart) std::cout << std::setw(2) << static_cast(c); std::cout << std::endl; @@ -307,7 +316,7 @@ try } std::cout << "[" << put_time << "] "; - if(passwords.empty()) + if (passwords.empty()) { std::cout << "Could not recover password" << std::endl; return 1; @@ -317,12 +326,12 @@ try std::cout << "Password" << std::endl; const auto flagsBefore = std::cout.setf(std::ios::hex, std::ios::basefield); - const auto fillBefore = std::cout.fill('0'); + const auto fillBefore = std::cout.fill('0'); - for(const auto& password : passwords) + for (const auto& password : passwords) { std::cout << "as bytes: "; - for(byte c : password) + for (byte c : password) std::cout << std::setw(2) << static_cast(c) << ' '; std::cout << std::endl; std::cout << "as text: " << password << std::endl; @@ -335,13 +344,13 @@ try return 0; } -catch(const Arguments::Error& e) +catch (const Arguments::Error& e) { std::cout << e.what() << std::endl; std::cout << "Run 'bkcrack -h' for help." << std::endl; return 1; } -catch(const BaseError& e) +catch (const BaseError& e) { std::cout << e.what() << std::endl; return 1; @@ -352,11 +361,14 @@ namespace std::string getEncryptionDescription(Zip::Encryption encryption) { - switch(encryption) + switch (encryption) { - case Zip::Encryption::None: return "None"; - case Zip::Encryption::Traditional: return "ZipCrypto"; - case Zip::Encryption::Unsupported: return "Other"; + case Zip::Encryption::None: + return "None"; + case Zip::Encryption::Traditional: + return "ZipCrypto"; + case Zip::Encryption::Unsupported: + return "Other"; } assert(false); @@ -365,9 +377,11 @@ std::string getEncryptionDescription(Zip::Encryption encryption) std::string getCompressionDescription(Zip::Compression compression) { - switch(compression) + switch (compression) { - #define CASE(c) case Zip::Compression::c: return #c +#define CASE(c) \ + case Zip::Compression::c: \ + return #c CASE(Store); CASE(Shrink); CASE(Implode); @@ -381,7 +395,7 @@ std::string getCompressionDescription(Zip::Compression compression) CASE(JPEG); CASE(WavPack); CASE(PPMd); - #undef CASE +#undef CASE } return "Other (" + std::to_string(static_cast(compression)) + ")"; @@ -392,15 +406,17 @@ void listEntries(const std::string& archiveFilename) auto archive = Zip{archiveFilename}; std::cout << "Archive: " << archiveFilename << "\n" - "Index Encryption Compression CRC32 Uncompressed Packed size Name\n" + << "Index Encryption Compression CRC32 Uncompressed Packed size Name\n" "----- ---------- ----------- -------- ------------ ------------ ----------------\n"; - const auto flagsBefore = std::cout.setf(std::ios::right | std::ios::dec, std::ios::adjustfield | std::ios::basefield); + const auto flagsBefore = + std::cout.setf(std::ios::right | std::ios::dec, std::ios::adjustfield | std::ios::basefield); const auto fillBefore = std::cout.fill(' '); std::size_t index = 0; - for(const auto& entry : archive) + for (const auto& entry : archive) { + // clang-format off std::cout << std::setw(5) << index++ << ' ' << std::left @@ -415,6 +431,7 @@ void listEntries(const std::string& archiveFilename) << std::setw(12) << entry.uncompressedSize << ' ' << std::setw(12) << entry.packedSize << ' ' << entry.name << '\n'; + // clang-format on } std::cout.fill(fillBefore); @@ -424,12 +441,13 @@ void listEntries(const std::string& archiveFilename) void decipher(std::istream& is, std::size_t size, std::size_t discard, std::ostream& os, Keys keys) { std::istreambuf_iterator cipher(is); - std::size_t i; + std::size_t i; - for(i = 0; i < discard && i < size && cipher != std::istreambuf_iterator(); i++, ++cipher) - keys.update(*cipher ^ keys.getK()); + for (i = 0; i < discard && i < size && cipher != std::istreambuf_iterator(); i++, ++cipher) + keys.update(*cipher ^ keys.getK()); - for(std::ostreambuf_iterator plain(os); i < size && cipher != std::istreambuf_iterator(); i++, ++cipher, ++plain) + for (std::ostreambuf_iterator plain(os); i < size && cipher != std::istreambuf_iterator(); + i++, ++cipher, ++plain) { byte p = *cipher ^ keys.getK(); keys.update(p); diff --git a/src/password.cpp b/src/password.cpp index 57814aa..54236a4 100644 --- a/src/password.cpp +++ b/src/password.cpp @@ -1,13 +1,20 @@ #include "password.hpp" + #include "Crc32Tab.hpp" #include "MultTab.hpp" + #include #include #include #include -Recovery::Recovery(const Keys& keys, const bytevec& charset, std::vector& solutions, std::mutex& solutionsMutex, bool exhaustive, Progress& progress) -: charset(charset), solutions(solutions), solutionsMutex(solutionsMutex), exhaustive(exhaustive), progress(progress) +Recovery::Recovery(const Keys& keys, const bytevec& charset, std::vector& solutions, + std::mutex& solutionsMutex, bool exhaustive, Progress& progress) +: charset(charset) +, solutions(solutions) +, solutionsMutex(solutionsMutex) +, exhaustive(exhaustive) +, progress(progress) { // initialize target X, Y and Z values x[6] = keys.getX(); @@ -18,17 +25,17 @@ Recovery::Recovery(const Keys& keys, const bytevec& charset, std::vector> 16]) + if (!z0_16_32[initial.getZ() >> 16]) return; // initialize starting X, Y and Z values x[0] = x0 = initial.getX(); - y[0] = initial.getY(); - z[0] = initial.getZ(); + y[0] = initial.getY(); + z[0] = initial.getZ(); // complete Z values and derive Y[24,32) values - for(int i = 1; i <= 4; i++) + for (int i = 1; i <= 4; i++) { - y[i] = Crc32Tab::getYi_24_32(z[i], z[i-1]); - z[i] = Crc32Tab::crc32(z[i-1], msb(y[i])); + y[i] = Crc32Tab::getYi_24_32(z[i], z[i - 1]); + z[i] = Crc32Tab::crc32(z[i - 1], msb(y[i])); } // recursively complete Y values and derive password @@ -66,10 +73,10 @@ void Recovery::recoverShortPassword(const Keys& initial) void Recovery::recoverLongPassword(const Keys& initial) { - if(prefix.size() + 7 == length) // there is only one more character to bruteforce + if (prefix.size() + 7 == length) // there is only one more character to bruteforce { // check compatible Z{-1}[24, 32) - if(!zm1_24_32[initial.getZ() >> 24]) + if (!zm1_24_32[initial.getZ() >> 24]) return; prefix.push_back(charset[0]); @@ -79,7 +86,7 @@ void Recovery::recoverLongPassword(const Keys& initial) const uint32 y0_partial = initial.getY() * MultTab::MULT + 1; const uint32 z0_partial = Crc32Tab::crc32(initial.getZ(), 0); - for(byte pi : charset) + for (byte pi : charset) { // finish to update the cipher state const uint32 x0 = x0_partial ^ Crc32Tab::crc32(0, pi); @@ -89,25 +96,25 @@ void Recovery::recoverLongPassword(const Keys& initial) // recoverShortPassword is inlined below for performance // check compatible Z0[16,32) - if(!z0_16_32[z0 >> 16]) + if (!z0_16_32[z0 >> 16]) continue; prefix.back() = pi; // initialize starting X, Y and Z values x[0] = this->x0 = x0; - y[0] = y0; - z[0] = z0; + y[0] = y0; + z[0] = z0; // complete Z values and derive Y[24,32) values - y[1] = Crc32Tab::getYi_24_32(z[1], z[1-1]); - z[1] = Crc32Tab::crc32(z[1-1], msb(y[1])); - y[2] = Crc32Tab::getYi_24_32(z[2], z[2-1]); - z[2] = Crc32Tab::crc32(z[2-1], msb(y[2])); - y[3] = Crc32Tab::getYi_24_32(z[3], z[3-1]); - z[3] = Crc32Tab::crc32(z[3-1], msb(y[3])); - y[4] = Crc32Tab::getYi_24_32(z[4], z[4-1]); - //z[4] = Crc32Tab::crc32(z[4-1], msb(y[4])); // this one is already known + y[1] = Crc32Tab::getYi_24_32(z[1], z[1 - 1]); + z[1] = Crc32Tab::crc32(z[1 - 1], msb(y[1])); + y[2] = Crc32Tab::getYi_24_32(z[2], z[2 - 1]); + z[2] = Crc32Tab::crc32(z[2 - 1], msb(y[2])); + y[3] = Crc32Tab::getYi_24_32(z[3], z[3 - 1]); + z[3] = Crc32Tab::crc32(z[3 - 1], msb(y[3])); + y[4] = Crc32Tab::getYi_24_32(z[4], z[4 - 1]); + // z[4] = Crc32Tab::crc32(z[4 - 1], msb(y[4])); // this one is already known // recursively complete Y values and derive password recursion(5); @@ -119,7 +126,7 @@ void Recovery::recoverLongPassword(const Keys& initial) { prefix.push_back(charset[0]); - for(byte pi : charset) + for (byte pi : charset) { Keys init = initial; init.update(pi); @@ -135,28 +142,28 @@ void Recovery::recoverLongPassword(const Keys& initial) void Recovery::recursion(int i) { - if(i != 1) // the Y-list is not complete so generate Y{i-1} values + if (i != 1) // the Y-list is not complete so generate Y{i-1} values { - uint32 fy = (y[i] - 1) * MultTab::MULTINV; + uint32 fy = (y[i] - 1) * MultTab::MULTINV; uint32 ffy = (fy - 1) * MultTab::MULTINV; // get possible LSB(Xi) - for(byte xi_0_8 : MultTab::getMsbProdFiber2(msb(ffy - (y[i-2] & MASK_24_32)))) + for (byte xi_0_8 : MultTab::getMsbProdFiber2(msb(ffy - (y[i - 2] & MASK_24_32)))) { // compute corresponding Y{i-1} uint32 yim1 = fy - xi_0_8; // filter values with Y{i-2}[24,32) - if(ffy - MultTab::getMultinv(xi_0_8) - (y[i-2] & MASK_24_32) <= MAXDIFF_0_24 - && msb(yim1) == msb(y[i-1])) + if (ffy - MultTab::getMultinv(xi_0_8) - (y[i - 2] & MASK_24_32) <= MAXDIFF_0_24 && + msb(yim1) == msb(y[i - 1])) { // add Y{i-1} to the Y-list - y[i-1] = yim1; + y[i - 1] = yim1; // set Xi value x[i] = xi_0_8; - recursion(i-1); + recursion(i - 1); } } } @@ -164,44 +171,45 @@ void Recovery::recursion(int i) { // only the X1 LSB was not set yet, so do it here x[1] = (y[1] - 1) * MultTab::MULTINV - y[0]; - if(x[1] > 0xff) + if (x[1] > 0xff) return; // complete X values and derive password - for(int i = 5; 0 <= i; i--) + for (int i = 5; 0 <= i; i--) { - uint32 xi_xor_pi = Crc32Tab::crc32inv(x[i+1], 0); - p[i] = lsb(xi_xor_pi ^ x[i]); - x[i] = xi_xor_pi ^ p[i]; + uint32 xi_xor_pi = Crc32Tab::crc32inv(x[i + 1], 0); + p[i] = lsb(xi_xor_pi ^ x[i]); + x[i] = xi_xor_pi ^ p[i]; } - if(x[0] == x0) // the password is successfully recovered + if (x[0] == x0) // the password is successfully recovered { std::string password = std::string(prefix.begin(), prefix.end()); password.append(p.begin(), p.end()); password.erase(password.begin(), password.end() - length); - const bool isInCharset = std::all_of(password.begin(), password.end(), [this](unsigned char c) { - return std::binary_search(charset.begin(), charset.end(), c); - }); + const bool isInCharset = + std::all_of(password.begin(), password.end(), + [this](unsigned char c) { return std::binary_search(charset.begin(), charset.end(), c); }); - if(!isInCharset) + if (!isInCharset) { - progress.log([&password](std::ostream& os) - { - const auto flagsBefore = os.setf(std::ios::hex, std::ios::basefield); - const auto fillBefore = os.fill('0'); + progress.log( + [&password](std::ostream& os) + { + const auto flagsBefore = os.setf(std::ios::hex, std::ios::basefield); + const auto fillBefore = os.fill('0'); - os << "Password: " << password << " (as bytes:"; - for(byte c : password) - os << ' ' << std::setw(2) << static_cast(c); - os << ')' << std::endl; + os << "Password: " << password << " (as bytes:"; + for (byte c : password) + os << ' ' << std::setw(2) << static_cast(c); + os << ')' << std::endl; - os.fill(fillBefore); - os.flags(flagsBefore); + os.fill(fillBefore); + os.flags(flagsBefore); - os << "Some characters are not in the expected charset. Continuing." << std::endl; - }); + os << "Some characters are not in the expected charset. Continuing." << std::endl; + }); return; } @@ -211,12 +219,9 @@ void Recovery::recursion(int i) solutions.push_back(password); } - progress.log([&password](std::ostream& os) - { - os << "Password: " << password << std::endl; - }); + progress.log([&password](std::ostream& os) { os << "Password: " << password << std::endl; }); - if(!exhaustive) + if (!exhaustive) progress.state = Progress::State::EarlyExit; } } @@ -225,28 +230,31 @@ void Recovery::recursion(int i) namespace { -void recoverPasswordRecursive(Recovery& worker, int jobs, const Keys& initial, const std::string& start, std::string& restart, Progress& progress) +void recoverPasswordRecursive(Recovery& worker, int jobs, const Keys& initial, const std::string& start, + std::string& restart, Progress& progress) { const int charsetSize = worker.charset.size(); int index_start = 0; - if(worker.prefix.size() < start.size()) - while(index_start < charsetSize && worker.charset[index_start] < static_cast(start[worker.prefix.size()])) + if (worker.prefix.size() < start.size()) + while (index_start < charsetSize && + worker.charset[index_start] < static_cast(start[worker.prefix.size()])) ++index_start; - if(worker.prefix.size() + 1 + 9 == worker.length) // bruteforce one character in parallel + if (worker.prefix.size() + 1 + 9 == worker.length) // bruteforce one character in parallel { worker.prefix.push_back(worker.charset[0]); progress.done += index_start * charsetSize; - const auto threadCount = std::clamp(jobs, 1, charsetSize); - auto threads = std::vector{}; - auto nextCandidateIndex = std::atomic{index_start}; - for(auto i = 0; i < threadCount; ++i) + const auto threadCount = std::clamp(jobs, 1, charsetSize); + auto threads = std::vector{}; + auto nextCandidateIndex = std::atomic{index_start}; + for (auto i = 0; i < threadCount; ++i) threads.emplace_back( - [&nextCandidateIndex, charsetSize, &progress, worker, initial]() mutable { - for(auto i = nextCandidateIndex++; i < charsetSize; i = nextCandidateIndex++) + [&nextCandidateIndex, charsetSize, &progress, worker, initial]() mutable + { + for (auto i = nextCandidateIndex++; i < charsetSize; i = nextCandidateIndex++) { byte pm4 = worker.charset[i]; @@ -259,50 +267,53 @@ void recoverPasswordRecursive(Recovery& worker, int jobs, const Keys& initial, c progress.done += charsetSize; - if(progress.state != Progress::State::Normal) + if (progress.state != Progress::State::Normal) break; } }); - for(auto& thread : threads) + for (auto& thread : threads) thread.join(); worker.prefix.pop_back(); - if(nextCandidateIndex < charsetSize) + if (nextCandidateIndex < charsetSize) { restart = worker.prefix; restart.push_back(worker.charset[nextCandidateIndex]); restart.append(worker.length - 6 - restart.size(), worker.charset[0]); } } - else if(worker.prefix.size() + 2 + 9 == worker.length) // bruteforce two characters in parallel + else if (worker.prefix.size() + 2 + 9 == worker.length) // bruteforce two characters in parallel { index_start *= charsetSize; - if(worker.prefix.size() + 1 < start.size()) + if (worker.prefix.size() + 1 < start.size()) { const auto maxIndex = std::min(charsetSize * charsetSize, index_start + charsetSize); - while(index_start < maxIndex && worker.charset[index_start % charsetSize] < static_cast(start[worker.prefix.size() + 1])) + while (index_start < maxIndex && worker.charset[index_start % charsetSize] < + static_cast(start[worker.prefix.size() + 1])) ++index_start; } worker.prefix.push_back(worker.charset[0]); worker.prefix.push_back(worker.charset[0]); - const bool reportProgress = worker.prefix.size() == 2; + const bool reportProgress = worker.prefix.size() == 2; const bool reportProgressCoarse = worker.prefix.size() == 3; - if(reportProgress) + if (reportProgress) progress.done += index_start; - else if(reportProgressCoarse) + else if (reportProgressCoarse) progress.done += index_start / charsetSize; - const auto threadCount = std::clamp(jobs, 1, charsetSize); - auto threads = std::vector{}; - auto nextCandidateIndex = std::atomic{index_start}; - for(auto i = 0; i < threadCount; ++i) + const auto threadCount = std::clamp(jobs, 1, charsetSize); + auto threads = std::vector{}; + auto nextCandidateIndex = std::atomic{index_start}; + for (auto i = 0; i < threadCount; ++i) threads.emplace_back( - [&nextCandidateIndex, charsetSize, &progress, worker, initial, reportProgress, reportProgressCoarse]() mutable { - for(auto i = nextCandidateIndex++; i < charsetSize * charsetSize; i = nextCandidateIndex++) + [&nextCandidateIndex, charsetSize, &progress, worker, initial, reportProgress, + reportProgressCoarse]() mutable + { + for (auto i = nextCandidateIndex++; i < charsetSize * charsetSize; i = nextCandidateIndex++) { byte pm4 = worker.charset[i / charsetSize]; byte pm3 = worker.charset[i % charsetSize]; @@ -316,20 +327,20 @@ void recoverPasswordRecursive(Recovery& worker, int jobs, const Keys& initial, c worker.recoverLongPassword(init); - if(reportProgress || (reportProgressCoarse && i % charsetSize == 0)) + if (reportProgress || (reportProgressCoarse && i % charsetSize == 0)) progress.done++; - if(progress.state != Progress::State::Normal) + if (progress.state != Progress::State::Normal) break; } }); - for(auto& thread : threads) + for (auto& thread : threads) thread.join(); worker.prefix.pop_back(); worker.prefix.pop_back(); - if(nextCandidateIndex < charsetSize * charsetSize) + if (nextCandidateIndex < charsetSize * charsetSize) { restart = worker.prefix; restart.push_back(worker.charset[nextCandidateIndex / charsetSize]); @@ -343,12 +354,12 @@ void recoverPasswordRecursive(Recovery& worker, int jobs, const Keys& initial, c const bool reportProgress = worker.prefix.size() == 2; - if(worker.prefix.size() == 1) + if (worker.prefix.size() == 1) progress.done += index_start * charsetSize; - else if(reportProgress) + else if (reportProgress) progress.done += index_start; - for(int i = index_start; i < charsetSize; i++) + for (int i = index_start; i < charsetSize; i++) { byte pi = worker.charset[i]; @@ -362,10 +373,10 @@ void recoverPasswordRecursive(Recovery& worker, int jobs, const Keys& initial, c // Because the recursive call may explore only a fraction of its // search space, check that it was run in full before counting progress. - if(!restart.empty()) + if (!restart.empty()) break; - if(reportProgress) + if (reportProgress) progress.done++; } @@ -375,27 +386,29 @@ void recoverPasswordRecursive(Recovery& worker, int jobs, const Keys& initial, c } // namespace -std::vector recoverPassword(const Keys& keys, const bytevec& charset, std::size_t minLength, std::size_t maxLength, std::string& start, int jobs, bool exhaustive, Progress& progress) +std::vector recoverPassword(const Keys& keys, const bytevec& charset, std::size_t minLength, + std::size_t maxLength, std::string& start, int jobs, bool exhaustive, + Progress& progress) { std::vector solutions; - std::mutex solutionsMutex; - Recovery worker(keys, charset, solutions, solutionsMutex, exhaustive, progress); + std::mutex solutionsMutex; + Recovery worker(keys, charset, solutions, solutionsMutex, exhaustive, progress); - std::string restart; + std::string restart; const std::size_t startLength = std::max(minLength, start.empty() ? 0 : start.size() + 6); - for(std::size_t length = startLength; length <= maxLength; length++) + for (std::size_t length = startLength; length <= maxLength; length++) { - if(progress.state != Progress::State::Normal) + if (progress.state != Progress::State::Normal) break; - if(length <= 6) + if (length <= 6) { progress.log([](std::ostream& os) { os << "length 0-6..." << std::endl; }); Keys initial; // look for a password of length between 0 and 6 - for(int l = 6; l >= 0; l--) + for (int l = 6; l >= 0; l--) { worker.length = l; worker.recoverShortPassword(initial); @@ -410,13 +423,13 @@ std::vector recoverPassword(const Keys& keys, const bytevec& charse progress.log([length](std::ostream& os) { os << "length " << length << "..." << std::endl; }); worker.length = length; - if(length < 10) + if (length < 10) { worker.recoverLongPassword(Keys{}); } else { - progress.done = 0; + progress.done = 0; progress.total = charset.size() * charset.size(); recoverPasswordRecursive(worker, jobs, Keys{}, length == startLength ? start : "", restart, progress); diff --git a/src/types.cpp b/src/types.cpp index f2e5b64..d0476f5 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1,5 +1,6 @@ #include "types.hpp" BaseError::BaseError(const std::string& type, const std::string& description) - : std::runtime_error(type + ": " + description + ".") -{} +: std::runtime_error(type + ": " + description + ".") +{ +} From 2e9955fe479f77050f6759fb56d4da41168b30e3 Mon Sep 17 00:00:00 2001 From: kimci86 Date: Tue, 6 Feb 2024 22:54:02 +0100 Subject: [PATCH 3/4] Add cmake target to format code in place --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e50644..34d02b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,16 @@ if(BKCRACK_BUILD_TESTING) add_subdirectory(tests) endif() +# code formatting +find_program(BKCRACK_CLANG_FORMAT_EXECUTABLE clang-format DOC "Path to clang-format program used to format C++ code.") +if(BKCRACK_CLANG_FORMAT_EXECUTABLE) + get_target_property(files_to_format bkcrack SOURCES) + add_custom_target(format + COMMAND ${BKCRACK_CLANG_FORMAT_EXECUTABLE} -i ${files_to_format} + COMMENT "Formatting C++ code with ${BKCRACK_CLANG_FORMAT_EXECUTABLE}" + VERBATIM) +endif() + # install rules install(DIRECTORY example DESTINATION .) install(DIRECTORY tools DESTINATION .) From 6b9218b8677ca25718e379867a841431325f661b Mon Sep 17 00:00:00 2001 From: kimci86 Date: Tue, 6 Feb 2024 22:55:22 +0100 Subject: [PATCH 4/4] Check formatting on CI --- .github/workflows/ci.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b90aff..33c0c23 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,10 +39,27 @@ jobs: build/*.zip build/*.tar.gz + format: + name: Formatting + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure project + run: cmake -S . -B build -DBKCRACK_CLANG_FORMAT_EXECUTABLE=/usr/bin/clang-format-15 + + - name: Format C++ code + run: cmake --build build --target format + + - name: Check for difference + run: git diff --exit-code + release: name: Release runs-on: ubuntu-latest - needs: build + needs: [build, format] if: startsWith(github.ref, 'refs/tags/') steps: