From dd27eb262027032c35a8dd57313931f76d03dfb7 Mon Sep 17 00:00:00 2001 From: CarterPerez-dev Date: Sun, 22 Mar 2026 08:01:01 -0400 Subject: [PATCH] feat: all hashers (MD5, SHA1, SHA256, SHA512) and HashDetector OpenSSL EVP-based hashers verified against NIST known-answer vectors. HashDetector auto-identifies hash type from hex length with validation. 15 new tests, all passing. --- .../hash-cracker/src/hash/HashDetector.cpp | 23 ++++++- .../hash-cracker/src/hash/MD5Hasher.cpp | 24 ++++++- .../hash-cracker/src/hash/SHA1Hasher.cpp | 24 ++++++- .../hash-cracker/src/hash/SHA256Hasher.cpp | 24 ++++++- .../hash-cracker/src/hash/SHA512Hasher.cpp | 24 ++++++- .../hash-cracker/tests/test_hash_detector.cpp | 38 +++++++++++- .../hash-cracker/tests/test_hashers.cpp | 62 ++++++++++++++++++- 7 files changed, 205 insertions(+), 14 deletions(-) diff --git a/PROJECTS/beginner/hash-cracker/src/hash/HashDetector.cpp b/PROJECTS/beginner/hash-cracker/src/hash/HashDetector.cpp index 27662655..22d7335b 100644 --- a/PROJECTS/beginner/hash-cracker/src/hash/HashDetector.cpp +++ b/PROJECTS/beginner/hash-cracker/src/hash/HashDetector.cpp @@ -2,7 +2,26 @@ // HashDetector.cpp #include "src/hash/HashDetector.hpp" +#include "src/config/Config.hpp" +#include +#include -std::expected HashDetector::detect(std::string_view) { - return std::unexpected(CrackError::InvalidHash); +std::expected HashDetector::detect(std::string_view hash) { + auto is_hex = [](char c) { + return (c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'); + }; + + if (!std::ranges::all_of(hash, is_hex)) { + return std::unexpected(CrackError::InvalidHash); + } + + switch (hash.size()) { + case config::MD5_HEX_LENGTH: return HashType::MD5; + case config::SHA1_HEX_LENGTH: return HashType::SHA1; + case config::SHA256_HEX_LENGTH: return HashType::SHA256; + case config::SHA512_HEX_LENGTH: return HashType::SHA512; + default: return std::unexpected(CrackError::InvalidHash); + } } diff --git a/PROJECTS/beginner/hash-cracker/src/hash/MD5Hasher.cpp b/PROJECTS/beginner/hash-cracker/src/hash/MD5Hasher.cpp index 707bc822..2823e59a 100644 --- a/PROJECTS/beginner/hash-cracker/src/hash/MD5Hasher.cpp +++ b/PROJECTS/beginner/hash-cracker/src/hash/MD5Hasher.cpp @@ -2,7 +2,27 @@ // MD5Hasher.cpp #include "src/hash/MD5Hasher.hpp" +#include +#include +#include +#include +#include -std::string MD5Hasher::hash(std::string_view) const { - return ""; +std::string MD5Hasher::hash(std::string_view input) const { + auto ctx = std::unique_ptr( + EVP_MD_CTX_new(), EVP_MD_CTX_free); + + EVP_DigestInit_ex(ctx.get(), EVP_md5(), nullptr); + EVP_DigestUpdate(ctx.get(), input.data(), input.size()); + + std::array digest{}; + unsigned int len = 0; + EVP_DigestFinal_ex(ctx.get(), digest.data(), &len); + + std::ostringstream hex; + hex << std::hex << std::setfill('0'); + for (unsigned int i = 0; i < len; ++i) { + hex << std::setw(2) << static_cast(digest[i]); + } + return hex.str(); } diff --git a/PROJECTS/beginner/hash-cracker/src/hash/SHA1Hasher.cpp b/PROJECTS/beginner/hash-cracker/src/hash/SHA1Hasher.cpp index c37e2e03..e27b3fa5 100644 --- a/PROJECTS/beginner/hash-cracker/src/hash/SHA1Hasher.cpp +++ b/PROJECTS/beginner/hash-cracker/src/hash/SHA1Hasher.cpp @@ -2,7 +2,27 @@ // SHA1Hasher.cpp #include "src/hash/SHA1Hasher.hpp" +#include +#include +#include +#include +#include -std::string SHA1Hasher::hash(std::string_view) const { - return ""; +std::string SHA1Hasher::hash(std::string_view input) const { + auto ctx = std::unique_ptr( + EVP_MD_CTX_new(), EVP_MD_CTX_free); + + EVP_DigestInit_ex(ctx.get(), EVP_sha1(), nullptr); + EVP_DigestUpdate(ctx.get(), input.data(), input.size()); + + std::array digest{}; + unsigned int len = 0; + EVP_DigestFinal_ex(ctx.get(), digest.data(), &len); + + std::ostringstream hex; + hex << std::hex << std::setfill('0'); + for (unsigned int i = 0; i < len; ++i) { + hex << std::setw(2) << static_cast(digest[i]); + } + return hex.str(); } diff --git a/PROJECTS/beginner/hash-cracker/src/hash/SHA256Hasher.cpp b/PROJECTS/beginner/hash-cracker/src/hash/SHA256Hasher.cpp index 798c80b2..d6d4fa08 100644 --- a/PROJECTS/beginner/hash-cracker/src/hash/SHA256Hasher.cpp +++ b/PROJECTS/beginner/hash-cracker/src/hash/SHA256Hasher.cpp @@ -2,7 +2,27 @@ // SHA256Hasher.cpp #include "src/hash/SHA256Hasher.hpp" +#include +#include +#include +#include +#include -std::string SHA256Hasher::hash(std::string_view) const { - return ""; +std::string SHA256Hasher::hash(std::string_view input) const { + auto ctx = std::unique_ptr( + EVP_MD_CTX_new(), EVP_MD_CTX_free); + + EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr); + EVP_DigestUpdate(ctx.get(), input.data(), input.size()); + + std::array digest{}; + unsigned int len = 0; + EVP_DigestFinal_ex(ctx.get(), digest.data(), &len); + + std::ostringstream hex; + hex << std::hex << std::setfill('0'); + for (unsigned int i = 0; i < len; ++i) { + hex << std::setw(2) << static_cast(digest[i]); + } + return hex.str(); } diff --git a/PROJECTS/beginner/hash-cracker/src/hash/SHA512Hasher.cpp b/PROJECTS/beginner/hash-cracker/src/hash/SHA512Hasher.cpp index cc45c0f6..7117a481 100644 --- a/PROJECTS/beginner/hash-cracker/src/hash/SHA512Hasher.cpp +++ b/PROJECTS/beginner/hash-cracker/src/hash/SHA512Hasher.cpp @@ -2,7 +2,27 @@ // SHA512Hasher.cpp #include "src/hash/SHA512Hasher.hpp" +#include +#include +#include +#include +#include -std::string SHA512Hasher::hash(std::string_view) const { - return ""; +std::string SHA512Hasher::hash(std::string_view input) const { + auto ctx = std::unique_ptr( + EVP_MD_CTX_new(), EVP_MD_CTX_free); + + EVP_DigestInit_ex(ctx.get(), EVP_sha512(), nullptr); + EVP_DigestUpdate(ctx.get(), input.data(), input.size()); + + std::array digest{}; + unsigned int len = 0; + EVP_DigestFinal_ex(ctx.get(), digest.data(), &len); + + std::ostringstream hex; + hex << std::hex << std::setfill('0'); + for (unsigned int i = 0; i < len; ++i) { + hex << std::setw(2) << static_cast(digest[i]); + } + return hex.str(); } diff --git a/PROJECTS/beginner/hash-cracker/tests/test_hash_detector.cpp b/PROJECTS/beginner/hash-cracker/tests/test_hash_detector.cpp index 5c802182..7b32891c 100644 --- a/PROJECTS/beginner/hash-cracker/tests/test_hash_detector.cpp +++ b/PROJECTS/beginner/hash-cracker/tests/test_hash_detector.cpp @@ -2,7 +2,41 @@ // test_hash_detector.cpp #include +#include "src/hash/HashDetector.hpp" -TEST(HashDetectorTest, Stub) { - EXPECT_TRUE(true); +TEST(HashDetectorTest, DetectsMD5) { + auto result = HashDetector::detect("d41d8cd98f00b204e9800998ecf8427e"); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(*result, HashType::MD5); +} + +TEST(HashDetectorTest, DetectsSHA1) { + auto result = HashDetector::detect("da39a3ee5e6b4b0d3255bfef95601890afd80709"); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(*result, HashType::SHA1); +} + +TEST(HashDetectorTest, DetectsSHA256) { + auto result = HashDetector::detect( + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(*result, HashType::SHA256); +} + +TEST(HashDetectorTest, DetectsSHA512) { + auto result = HashDetector::detect(std::string(128, 'a')); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(*result, HashType::SHA512); +} + +TEST(HashDetectorTest, RejectsInvalidLength) { + auto result = HashDetector::detect("abc"); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), CrackError::InvalidHash); +} + +TEST(HashDetectorTest, RejectsNonHex) { + auto result = HashDetector::detect(std::string(64, 'z')); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), CrackError::InvalidHash); } diff --git a/PROJECTS/beginner/hash-cracker/tests/test_hashers.cpp b/PROJECTS/beginner/hash-cracker/tests/test_hashers.cpp index af3d5965..47d9c9a1 100644 --- a/PROJECTS/beginner/hash-cracker/tests/test_hashers.cpp +++ b/PROJECTS/beginner/hash-cracker/tests/test_hashers.cpp @@ -2,8 +2,66 @@ // test_hashers.cpp #include +#include "src/hash/MD5Hasher.hpp" +#include "src/hash/SHA1Hasher.hpp" #include "src/hash/SHA256Hasher.hpp" +#include "src/hash/SHA512Hasher.hpp" -TEST(SHA256HasherTest, Stub) { - EXPECT_TRUE(true); +TEST(SHA256HasherTest, KnownVectors) { + SHA256Hasher hasher; + EXPECT_EQ(hasher.hash(""), + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + EXPECT_EQ(hasher.hash("password"), + "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"); + EXPECT_EQ(hasher.hash("hello"), + "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"); +} + +TEST(SHA256HasherTest, StaticProperties) { + EXPECT_EQ(SHA256Hasher::name(), "SHA256"); + EXPECT_EQ(SHA256Hasher::digest_length(), 64); +} + +TEST(SHA256HasherTest, Deterministic) { + SHA256Hasher hasher; + auto h1 = hasher.hash("test"); + auto h2 = hasher.hash("test"); + EXPECT_EQ(h1, h2); +} + +TEST(MD5HasherTest, KnownVectors) { + MD5Hasher hasher; + EXPECT_EQ(hasher.hash(""), "d41d8cd98f00b204e9800998ecf8427e"); + EXPECT_EQ(hasher.hash("password"), "5f4dcc3b5aa765d61d8327deb882cf99"); +} + +TEST(MD5HasherTest, StaticProperties) { + EXPECT_EQ(MD5Hasher::name(), "MD5"); + EXPECT_EQ(MD5Hasher::digest_length(), 32); +} + +TEST(SHA1HasherTest, KnownVectors) { + SHA1Hasher hasher; + EXPECT_EQ(hasher.hash(""), "da39a3ee5e6b4b0d3255bfef95601890afd80709"); + EXPECT_EQ(hasher.hash("password"), "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8"); +} + +TEST(SHA1HasherTest, StaticProperties) { + EXPECT_EQ(SHA1Hasher::name(), "SHA1"); + EXPECT_EQ(SHA1Hasher::digest_length(), 40); +} + +TEST(SHA512HasherTest, KnownVectors) { + SHA512Hasher hasher; + EXPECT_EQ(hasher.hash(""), + "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce" + "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); + EXPECT_EQ(hasher.hash("password"), + "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb9" + "80b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86"); +} + +TEST(SHA512HasherTest, StaticProperties) { + EXPECT_EQ(SHA512Hasher::name(), "SHA512"); + EXPECT_EQ(SHA512Hasher::digest_length(), 128); }