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.
This commit is contained in:
parent
4771d29342
commit
dd27eb2620
|
|
@ -2,7 +2,26 @@
|
|||
// HashDetector.cpp
|
||||
|
||||
#include "src/hash/HashDetector.hpp"
|
||||
#include "src/config/Config.hpp"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
std::expected<HashType, CrackError> HashDetector::detect(std::string_view) {
|
||||
return std::unexpected(CrackError::InvalidHash);
|
||||
std::expected<HashType, CrackError> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,27 @@
|
|||
// MD5Hasher.cpp
|
||||
|
||||
#include "src/hash/MD5Hasher.hpp"
|
||||
#include <array>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <openssl/evp.h>
|
||||
#include <sstream>
|
||||
|
||||
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, decltype(&EVP_MD_CTX_free)>(
|
||||
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<unsigned char, EVP_MAX_MD_SIZE> 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<int>(digest[i]);
|
||||
}
|
||||
return hex.str();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,27 @@
|
|||
// SHA1Hasher.cpp
|
||||
|
||||
#include "src/hash/SHA1Hasher.hpp"
|
||||
#include <array>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <openssl/evp.h>
|
||||
#include <sstream>
|
||||
|
||||
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, decltype(&EVP_MD_CTX_free)>(
|
||||
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<unsigned char, EVP_MAX_MD_SIZE> 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<int>(digest[i]);
|
||||
}
|
||||
return hex.str();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,27 @@
|
|||
// SHA256Hasher.cpp
|
||||
|
||||
#include "src/hash/SHA256Hasher.hpp"
|
||||
#include <array>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <openssl/evp.h>
|
||||
#include <sstream>
|
||||
|
||||
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, decltype(&EVP_MD_CTX_free)>(
|
||||
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<unsigned char, EVP_MAX_MD_SIZE> 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<int>(digest[i]);
|
||||
}
|
||||
return hex.str();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,27 @@
|
|||
// SHA512Hasher.cpp
|
||||
|
||||
#include "src/hash/SHA512Hasher.hpp"
|
||||
#include <array>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <openssl/evp.h>
|
||||
#include <sstream>
|
||||
|
||||
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, decltype(&EVP_MD_CTX_free)>(
|
||||
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<unsigned char, EVP_MAX_MD_SIZE> 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<int>(digest[i]);
|
||||
}
|
||||
return hex.str();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,41 @@
|
|||
// test_hash_detector.cpp
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,66 @@
|
|||
// test_hashers.cpp
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue