diff --git a/include/Arguments.hpp b/include/Arguments.hpp index ed4b0b0..faa05e5 100644 --- a/include/Arguments.hpp +++ b/include/Arguments.hpp @@ -54,6 +54,9 @@ class Arguments /// 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; @@ -137,6 +140,7 @@ class Arguments offset, extraPlaintext, ignoreCheckByte, + attackStart, password, keys, decipheredFile, diff --git a/include/Attack.hpp b/include/Attack.hpp index da430cc..c22281c 100644 --- a/include/Attack.hpp +++ b/include/Attack.hpp @@ -63,11 +63,13 @@ class Attack /// \brief Iterate on Zi[2,32) candidates to try and find complete internal keys /// \param data Data used to carry out the attack /// \param zi_2_32_vector Zi[2,32) candidates +/// \param start Starting index of Zi[2,32) candidates in zi_2_32_vector to try. +/// Also used as an output parameter to tell where to restart. /// \param index Index of the Zi[2,32) values relative to keystream /// \param jobs Number of threads to use /// \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, 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/src/Arguments.cpp b/src/Arguments.cpp index 82d5e49..cb41afb 100644 --- a/src/Arguments.cpp +++ b/src/Arguments.cpp @@ -238,6 +238,9 @@ void Arguments::parseArgument() case Option::ignoreCheckByte: ignoreCheckByte = true; break; + case Option::attackStart: + attackStart = readInt("checkpoint"); + break; case Option::password: password = readString("password"); break; @@ -317,6 +320,7 @@ Arguments::Option Arguments::readOption(const std::string& description) PAIRS(-o, --offset, offset), PAIRS(-x, --extra, extraPlaintext), PAIR ( --ignore-check-byte, ignoreCheckByte), + PAIR ( --continue-attack, attackStart), PAIR ( --password, password), PAIRS(-k, --keys, keys), PAIRS(-d, --decipher, decipheredFile), diff --git a/src/Attack.cpp b/src/Attack.cpp index f6825ab..100e05a 100644 --- a/src/Attack.cpp +++ b/src/Attack.cpp @@ -177,7 +177,7 @@ void Attack::testXlist() progress.state = Progress::State::EarlyExit; } -std::vector attack(const Data& data, const u32vec& zi_2_32_vector, 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()); @@ -186,23 +186,28 @@ std::vector attack(const Data& data, const u32vec& zi_2_32_vector, std::si std::mutex solutionsMutex; Attack worker(data, index, solutions, solutionsMutex, exhaustive, progress); - progress.done = 0; + progress.done = start; progress.total = size; const auto threadCount = std::clamp(jobs, 1, size); auto threads = std::vector{}; - auto nextCandidateIndex = std::atomic{0}; + 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 && progress.state == Progress::State::Normal; i = nextCandidateIndex++) + for(auto i = nextCandidateIndex++; i < size; i = nextCandidateIndex++) { worker.carryout(candidates[i]); progress.done++; + + if(progress.state != Progress::State::Normal) + break; } }); for(auto& thread : threads) thread.join(); + start = nextCandidateIndex; + return solutions; } diff --git a/src/main.cpp b/src/main.cpp index 4427847..066e677 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,10 +36,14 @@ Options to get the internal password representation: --ignore-check-byte Do not automatically use ciphertext's check byte as known plaintext - --password Password from which to derive the internal password - representation. Useful for testing purposes and - advanced scenarios such as reverting the effect of - the --change-password command. + --continue-attack + Starting point of the attack. Useful to continue a previous + non-exhaustive or interrupted attack. + + --password + Password from which to derive the internal password representation. + Useful for testing purposes and advanced scenarios such as reverting + the effect of the --change-password command. Options to use the internal password representation: -k, --keys Internal password representation as three 32-bits @@ -144,12 +148,13 @@ try 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; - const auto state = [&]() -> Progress::State + const auto [state, restart] = [&]() -> std::pair { + int start = args.attackStart; ConsoleProgress progress(std::cout); SigintHandler sigintHandler{progress.state}; - keysvec = attack(data, zr.getCandidates(), zr.getIndex(), args.jobs, args.exhaustive, progress); - return progress.state; + keysvec = attack(data, zr.getCandidates(), start, zr.getIndex(), args.jobs, args.exhaustive, progress); + return {progress.state, start}; }(); if(state == Progress::State::Canceled) @@ -162,6 +167,8 @@ try if(keysvec.empty()) { std::cout << "Could not find the keys." << std::endl; + if(state == Progress::State::Canceled) + std::cout << "You may resume the attack with the option: --continue-attack " << restart << std::endl; return 1; } else