feat: project scaffolding with CMake, configs, stubs, and core types

Includes CMakeLists.txt, CMakePresets.json, .clang-format, .clang-tidy,
Config.hpp (all constants), Concepts.hpp (Hasher/AttackStrategy concepts),
and stub implementations for all source files. All 6 stub tests pass.
This commit is contained in:
CarterPerez-dev 2026-03-21 19:37:20 -04:00
parent 6b347d949a
commit 4771d29342
36 changed files with 736 additions and 0 deletions

View File

@ -0,0 +1,14 @@
# ©AngelaMos | 2026
# .clang-format
BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
UseTab: Never
ColumnLimit: 100
BreakBeforeBraces: Attach
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
IndentCaseLabels: true
NamespaceIndentation: None

View File

@ -0,0 +1,15 @@
# ©AngelaMos | 2026
# .clang-tidy
Checks: >
bugprone-*, cert-*, clang-analyzer-*, cppcoreguidelines-*, performance-*,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-pro-type-union-access,
-cert-err33-c,
-bugprone-easily-swappable-parameters
WarningsAsErrors: "*"

View File

@ -0,0 +1,19 @@
# ©AngelaMos | 2026
# .gitignore
docs/
build/
*.o
*.a
*.so
*.out
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
compile_commands.json
.cache/
.vscode/
.idea/
*.swp
*.swo
.DS_Store

View File

@ -0,0 +1,78 @@
# ©AngelaMos | 2026
# CMakeLists.txt
cmake_minimum_required(VERSION 3.25...4.2)
project(
hashcracker
VERSION 1.0.0
DESCRIPTION "Multi-threaded hash cracking tool"
LANGUAGES CXX
)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_library(project_warnings INTERFACE)
target_compile_options(project_warnings INTERFACE
-Wall -Wextra -Wpedantic -Wshadow -Wconversion
-Wnon-virtual-dtor -Wold-style-cast -Wcast-align
-Woverloaded-virtual -Wsign-conversion
)
find_package(OpenSSL REQUIRED)
find_package(Boost REQUIRED COMPONENTS program_options)
set(HASHCRACKER_SOURCES
src/hash/MD5Hasher.cpp
src/hash/SHA1Hasher.cpp
src/hash/SHA256Hasher.cpp
src/hash/SHA512Hasher.cpp
src/hash/HashDetector.cpp
src/attack/DictionaryAttack.cpp
src/attack/BruteForceAttack.cpp
src/attack/RuleAttack.cpp
src/rules/RuleSet.cpp
src/threading/ThreadPool.cpp
src/display/Progress.cpp
)
add_library(hashcracker_lib STATIC ${HASHCRACKER_SOURCES})
target_include_directories(hashcracker_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(hashcracker_lib
PUBLIC OpenSSL::Crypto project_warnings
)
target_compile_features(hashcracker_lib PUBLIC cxx_std_23)
add_executable(hashcracker main.cpp)
target_link_libraries(hashcracker
PRIVATE hashcracker_lib Boost::program_options project_warnings
)
include(FetchContent)
FetchContent_Declare(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.15.2
GIT_SHALLOW TRUE
SYSTEM
)
FetchContent_MakeAvailable(googletest)
enable_testing()
set(HASHCRACKER_TESTS
tests/test_hashers.cpp
tests/test_hash_detector.cpp
tests/test_dictionary_attack.cpp
tests/test_bruteforce_attack.cpp
tests/test_rules.cpp
tests/test_engine.cpp
)
add_executable(hashcracker_tests ${HASHCRACKER_TESTS})
target_link_libraries(hashcracker_tests
PRIVATE hashcracker_lib GTest::gtest_main
)
include(GoogleTest)
gtest_discover_tests(hashcracker_tests)

View File

@ -0,0 +1,29 @@
{
"version": 6,
"configurePresets": [
{
"name": "debug",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "release",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
],
"buildPresets": [
{ "name": "debug", "configurePreset": "debug" },
{ "name": "release", "configurePreset": "release" }
],
"testPresets": [
{ "name": "debug", "configurePreset": "debug", "output": { "outputOnFailure": true } }
]
}

View File

@ -0,0 +1,6 @@
// ©AngelaMos | 2026
// main.cpp
int main() {
return 0;
}

View File

@ -0,0 +1,14 @@
// ©AngelaMos | 2026
// BruteForceAttack.cpp
#include "src/attack/BruteForceAttack.hpp"
BruteForceAttack::BruteForceAttack(std::string_view, std::size_t,
unsigned, unsigned) {}
std::expected<std::string, AttackComplete> BruteForceAttack::next() {
return std::unexpected(AttackComplete{});
}
std::size_t BruteForceAttack::total() const { return total_; }
std::size_t BruteForceAttack::progress() const { return current_; }

View File

@ -0,0 +1,24 @@
// ©AngelaMos | 2026
// BruteForceAttack.hpp
#pragma once
#include <cstddef>
#include <expected>
#include <string>
#include <string_view>
#include "src/core/Concepts.hpp"
class BruteForceAttack {
public:
BruteForceAttack(std::string_view charset, std::size_t max_length,
unsigned thread_index, unsigned total_threads);
std::expected<std::string, AttackComplete> next();
std::size_t total() const;
std::size_t progress() const;
private:
std::size_t total_ = 0;
std::size_t current_ = 0;
};

View File

@ -0,0 +1,16 @@
// ©AngelaMos | 2026
// DictionaryAttack.cpp
#include "src/attack/DictionaryAttack.hpp"
std::expected<DictionaryAttack, CrackError> DictionaryAttack::create(
std::string_view, unsigned, unsigned) {
return std::unexpected(CrackError::FileNotFound);
}
std::expected<std::string, AttackComplete> DictionaryAttack::next() {
return std::unexpected(AttackComplete{});
}
std::size_t DictionaryAttack::total() const { return 0; }
std::size_t DictionaryAttack::progress() const { return 0; }

View File

@ -0,0 +1,23 @@
// ©AngelaMos | 2026
// DictionaryAttack.hpp
#pragma once
#include <cstddef>
#include <expected>
#include <string>
#include <string_view>
#include "src/core/Concepts.hpp"
class DictionaryAttack {
public:
static std::expected<DictionaryAttack, CrackError> create(
std::string_view path, unsigned thread_index, unsigned total_threads);
std::expected<std::string, AttackComplete> next();
std::size_t total() const;
std::size_t progress() const;
private:
DictionaryAttack() = default;
};

View File

@ -0,0 +1,16 @@
// ©AngelaMos | 2026
// RuleAttack.cpp
#include "src/attack/RuleAttack.hpp"
std::expected<RuleAttack, CrackError> RuleAttack::create(
std::string_view, bool, unsigned, unsigned) {
return std::unexpected(CrackError::FileNotFound);
}
std::expected<std::string, AttackComplete> RuleAttack::next() {
return std::unexpected(AttackComplete{});
}
std::size_t RuleAttack::total() const { return 0; }
std::size_t RuleAttack::progress() const { return 0; }

View File

@ -0,0 +1,24 @@
// ©AngelaMos | 2026
// RuleAttack.hpp
#pragma once
#include <cstddef>
#include <expected>
#include <string>
#include <string_view>
#include "src/core/Concepts.hpp"
class RuleAttack {
public:
static std::expected<RuleAttack, CrackError> create(
std::string_view path, bool chain_rules,
unsigned thread_index, unsigned total_threads);
std::expected<std::string, AttackComplete> next();
std::size_t total() const;
std::size_t progress() const;
private:
RuleAttack() = default;
};

View File

@ -0,0 +1,96 @@
// ©AngelaMos | 2026
// Config.hpp
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
namespace config {
constexpr std::string_view VERSION = "1.0.0";
constexpr std::string_view APP_NAME = "HASHCRACKER";
constexpr unsigned DEFAULT_THREAD_COUNT = 0;
constexpr std::size_t DEFAULT_MAX_BRUTE_LENGTH = 6;
constexpr int PROGRESS_UPDATE_MS = 100;
constexpr std::size_t PROGRESS_BAR_MIN_WIDTH = 20;
constexpr std::string_view CHARSET_LOWER = "abcdefghijklmnopqrstuvwxyz";
constexpr std::string_view CHARSET_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
constexpr std::string_view CHARSET_DIGITS = "0123456789";
constexpr std::string_view CHARSET_SPECIAL = "!@#$%^&*()-_=+[]{}|;:,.<>?/~`";
constexpr std::size_t MD5_HEX_LENGTH = 32;
constexpr std::size_t SHA1_HEX_LENGTH = 40;
constexpr std::size_t SHA256_HEX_LENGTH = 64;
constexpr std::size_t SHA512_HEX_LENGTH = 128;
constexpr std::size_t MAX_APPEND_DIGIT = 999;
constexpr std::size_t MAX_PREPEND_DIGIT = 999;
namespace color {
constexpr std::string_view RESET = "\033[0m";
constexpr std::string_view RED = "\033[31m";
constexpr std::string_view GREEN = "\033[32m";
constexpr std::string_view YELLOW = "\033[33m";
constexpr std::string_view CYAN = "\033[36m";
constexpr std::string_view BOLD = "\033[1m";
constexpr std::string_view DIM = "\033[2m";
}
namespace box {
constexpr std::string_view TOP_LEFT = "\u250C";
constexpr std::string_view TOP_RIGHT = "\u2510";
constexpr std::string_view BOTTOM_LEFT = "\u2514";
constexpr std::string_view BOTTOM_RIGHT = "\u2518";
constexpr std::string_view HORIZONTAL = "\u2500";
constexpr std::string_view VERTICAL = "\u2502";
constexpr std::string_view BLOCK_FULL = "\u2588";
constexpr std::string_view BLOCK_EMPTY = "\u2591";
constexpr std::string_view BAR_LEFT = "\u2590";
constexpr std::string_view BAR_RIGHT = "\u258C";
}
namespace icon {
constexpr std::string_view BOLT = "\u26A1";
constexpr std::string_view TIMER = "\u23F1";
constexpr std::string_view HOURGLASS = "\u23F3";
constexpr std::string_view CHART = "\U0001F4CA";
constexpr std::string_view CHECK = "\u2714";
constexpr std::string_view CROSS = "\u2716";
}
}
struct CrackConfig {
std::string target_hash;
std::string hash_type = "auto";
std::string wordlist_path;
std::string charset;
std::string salt;
std::string salt_position = "prepend";
std::string output_path;
std::size_t max_length = config::DEFAULT_MAX_BRUTE_LENGTH;
unsigned thread_count = config::DEFAULT_THREAD_COUNT;
bool bruteforce = false;
bool use_rules = false;
bool chain_rules = false;
};
struct CrackResult {
std::string plaintext;
std::string hash;
std::string algorithm;
double elapsed_seconds;
std::size_t candidates_tested;
double hashes_per_second;
};

View File

@ -0,0 +1,47 @@
// ©AngelaMos | 2026
// Concepts.hpp
#pragma once
#include <concepts>
#include <cstddef>
#include <expected>
#include <string>
#include <string_view>
struct AttackComplete {};
enum class CrackError {
FileNotFound,
InvalidHash,
UnsupportedAlgorithm,
OpenSSLError,
InvalidConfig,
Exhausted
};
constexpr std::string_view crack_error_message(CrackError e) {
switch (e) {
case CrackError::FileNotFound: return "File not found";
case CrackError::InvalidHash: return "Invalid hash format";
case CrackError::UnsupportedAlgorithm: return "Unsupported hash algorithm";
case CrackError::OpenSSLError: return "OpenSSL internal error";
case CrackError::InvalidConfig: return "Invalid configuration";
case CrackError::Exhausted: return "All candidates exhausted";
}
return "Unknown error";
}
template <typename T>
concept Hasher = requires(T h, std::string_view input) {
{ h.hash(input) } -> std::same_as<std::string>;
{ T::name() } -> std::convertible_to<std::string_view>;
{ T::digest_length() } -> std::same_as<std::size_t>;
};
template <typename T>
concept AttackStrategy = requires(T a) {
{ a.next() } -> std::same_as<std::expected<std::string, AttackComplete>>;
{ a.total() } -> std::same_as<std::size_t>;
{ a.progress() } -> std::same_as<std::size_t>;
};

View File

@ -0,0 +1,24 @@
// ©AngelaMos | 2026
// Progress.cpp
#include "src/display/Progress.hpp"
#include "src/config/Config.hpp"
Progress::Progress(std::string_view algorithm, std::string_view attack_mode,
unsigned thread_count, std::size_t total_candidates,
const std::atomic<bool>& found,
const std::atomic<std::size_t>& tested)
: algorithm_(algorithm), attack_mode_(attack_mode),
thread_count_(thread_count), total_(total_candidates),
found_(found), tested_(tested) {}
void Progress::print_banner() const {}
void Progress::update() {}
void Progress::print_cracked(const CrackResult&) const {}
void Progress::print_exhausted(std::string_view, std::string_view) const {}
bool Progress::is_tty() { return false; }
std::size_t Progress::terminal_width() { return 80; }
std::string Progress::render_bar(double, std::size_t) const { return ""; }
std::string Progress::format_count(std::size_t) { return ""; }
std::string Progress::format_time(double) { return ""; }
std::string Progress::format_speed(double) { return ""; }

View File

@ -0,0 +1,39 @@
// ©AngelaMos | 2026
// Progress.hpp
#pragma once
#include <atomic>
#include <cstddef>
#include <string>
#include <string_view>
struct CrackResult;
class Progress {
public:
Progress(std::string_view algorithm, std::string_view attack_mode,
unsigned thread_count, std::size_t total_candidates,
const std::atomic<bool>& found,
const std::atomic<std::size_t>& tested);
void print_banner() const;
void update();
void print_cracked(const CrackResult& result) const;
void print_exhausted(std::string_view hash, std::string_view algorithm) const;
static bool is_tty();
private:
std::string algorithm_;
std::string attack_mode_;
unsigned thread_count_;
std::size_t total_;
const std::atomic<bool>& found_;
const std::atomic<std::size_t>& tested_;
static std::size_t terminal_width();
std::string render_bar(double fraction, std::size_t width) const;
static std::string format_count(std::size_t n);
static std::string format_time(double seconds);
static std::string format_speed(double hps);
};

View File

@ -0,0 +1,8 @@
// ©AngelaMos | 2026
// HashDetector.cpp
#include "src/hash/HashDetector.hpp"
std::expected<HashType, CrackError> HashDetector::detect(std::string_view) {
return std::unexpected(CrackError::InvalidHash);
}

View File

@ -0,0 +1,15 @@
// ©AngelaMos | 2026
// HashDetector.hpp
#pragma once
#include <expected>
#include <string_view>
#include "src/core/Concepts.hpp"
enum class HashType { MD5, SHA1, SHA256, SHA512 };
class HashDetector {
public:
static std::expected<HashType, CrackError> detect(std::string_view hash);
};

View File

@ -0,0 +1,8 @@
// ©AngelaMos | 2026
// MD5Hasher.cpp
#include "src/hash/MD5Hasher.hpp"
std::string MD5Hasher::hash(std::string_view) const {
return "";
}

View File

@ -0,0 +1,15 @@
// ©AngelaMos | 2026
// MD5Hasher.hpp
#pragma once
#include <cstddef>
#include <string>
#include <string_view>
class MD5Hasher {
public:
std::string hash(std::string_view input) const;
static constexpr std::string_view name() { return "MD5"; }
static constexpr std::size_t digest_length() { return 32; }
};

View File

@ -0,0 +1,8 @@
// ©AngelaMos | 2026
// SHA1Hasher.cpp
#include "src/hash/SHA1Hasher.hpp"
std::string SHA1Hasher::hash(std::string_view) const {
return "";
}

View File

@ -0,0 +1,15 @@
// ©AngelaMos | 2026
// SHA1Hasher.hpp
#pragma once
#include <cstddef>
#include <string>
#include <string_view>
class SHA1Hasher {
public:
std::string hash(std::string_view input) const;
static constexpr std::string_view name() { return "SHA1"; }
static constexpr std::size_t digest_length() { return 40; }
};

View File

@ -0,0 +1,8 @@
// ©AngelaMos | 2026
// SHA256Hasher.cpp
#include "src/hash/SHA256Hasher.hpp"
std::string SHA256Hasher::hash(std::string_view) const {
return "";
}

View File

@ -0,0 +1,15 @@
// ©AngelaMos | 2026
// SHA256Hasher.hpp
#pragma once
#include <cstddef>
#include <string>
#include <string_view>
class SHA256Hasher {
public:
std::string hash(std::string_view input) const;
static constexpr std::string_view name() { return "SHA256"; }
static constexpr std::size_t digest_length() { return 64; }
};

View File

@ -0,0 +1,8 @@
// ©AngelaMos | 2026
// SHA512Hasher.cpp
#include "src/hash/SHA512Hasher.hpp"
std::string SHA512Hasher::hash(std::string_view) const {
return "";
}

View File

@ -0,0 +1,15 @@
// ©AngelaMos | 2026
// SHA512Hasher.hpp
#pragma once
#include <cstddef>
#include <string>
#include <string_view>
class SHA512Hasher {
public:
std::string hash(std::string_view input) const;
static constexpr std::string_view name() { return "SHA512"; }
static constexpr std::size_t digest_length() { return 128; }
};

View File

@ -0,0 +1,13 @@
// ©AngelaMos | 2026
// RuleSet.cpp
#include "src/rules/RuleSet.hpp"
std::generator<std::string> RuleSet::capitalize_first(std::string_view) { co_return; }
std::generator<std::string> RuleSet::uppercase_all(std::string_view) { co_return; }
std::generator<std::string> RuleSet::leet_speak(std::string_view) { co_return; }
std::generator<std::string> RuleSet::append_digits(std::string_view) { co_return; }
std::generator<std::string> RuleSet::prepend_digits(std::string_view) { co_return; }
std::generator<std::string> RuleSet::reverse(std::string_view) { co_return; }
std::generator<std::string> RuleSet::toggle_case(std::string_view) { co_return; }
std::generator<std::string> RuleSet::apply_all(std::string_view) { co_return; }

View File

@ -0,0 +1,20 @@
// ©AngelaMos | 2026
// RuleSet.hpp
#pragma once
#include <generator>
#include <string>
#include <string_view>
class RuleSet {
public:
static std::generator<std::string> capitalize_first(std::string_view word);
static std::generator<std::string> uppercase_all(std::string_view word);
static std::generator<std::string> leet_speak(std::string_view word);
static std::generator<std::string> append_digits(std::string_view word);
static std::generator<std::string> prepend_digits(std::string_view word);
static std::generator<std::string> reverse(std::string_view word);
static std::generator<std::string> toggle_case(std::string_view word);
static std::generator<std::string> apply_all(std::string_view word);
};

View File

@ -0,0 +1,20 @@
// ©AngelaMos | 2026
// ThreadPool.cpp
#include "src/threading/ThreadPool.hpp"
void SharedState::set_result(std::string plaintext) {
found.store(true, std::memory_order_relaxed);
auto lock = std::lock_guard{result_mutex};
if (!result.has_value()) {
result = std::move(plaintext);
}
}
ThreadPool::ThreadPool(unsigned thread_count)
: thread_count_(thread_count > 0 ? thread_count
: std::thread::hardware_concurrency()) {}
void ThreadPool::run(WorkFn) {}
SharedState& ThreadPool::state() { return state_; }

View File

@ -0,0 +1,35 @@
// ©AngelaMos | 2026
// ThreadPool.hpp
#pragma once
#include <atomic>
#include <functional>
#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <vector>
struct SharedState {
std::atomic<bool> found{false};
std::atomic<std::size_t> tested_count{0};
std::mutex result_mutex;
std::optional<std::string> result;
void set_result(std::string plaintext);
};
class ThreadPool {
public:
using WorkFn = std::function<void(unsigned thread_id, unsigned total, SharedState&)>;
explicit ThreadPool(unsigned thread_count);
void run(WorkFn work);
SharedState& state();
private:
unsigned thread_count_;
SharedState state_;
};

View File

@ -0,0 +1,8 @@
// ©AngelaMos | 2026
// test_bruteforce_attack.cpp
#include <gtest/gtest.h>
TEST(BruteForceAttackTest, Stub) {
EXPECT_TRUE(true);
}

View File

@ -0,0 +1,8 @@
// ©AngelaMos | 2026
// test_dictionary_attack.cpp
#include <gtest/gtest.h>
TEST(DictionaryAttackTest, Stub) {
EXPECT_TRUE(true);
}

View File

@ -0,0 +1,8 @@
// ©AngelaMos | 2026
// test_engine.cpp
#include <gtest/gtest.h>
TEST(EngineTest, Stub) {
EXPECT_TRUE(true);
}

View File

@ -0,0 +1,8 @@
// ©AngelaMos | 2026
// test_hash_detector.cpp
#include <gtest/gtest.h>
TEST(HashDetectorTest, Stub) {
EXPECT_TRUE(true);
}

View File

@ -0,0 +1,9 @@
// ©AngelaMos | 2026
// test_hashers.cpp
#include <gtest/gtest.h>
#include "src/hash/SHA256Hasher.hpp"
TEST(SHA256HasherTest, Stub) {
EXPECT_TRUE(true);
}

View File

@ -0,0 +1,8 @@
// ©AngelaMos | 2026
// test_rules.cpp
#include <gtest/gtest.h>
TEST(RuleSetTest, Stub) {
EXPECT_TRUE(true);
}