Add HMAC as per RFC 2104 draft implementation

This commit is contained in:
hashlag
2025-08-21 02:09:30 +03:00
parent 6c2feef644
commit be03a5314c
5 changed files with 181 additions and 5 deletions

View File

@@ -162,6 +162,10 @@ struct Md4Hash : public Hash<Md4Hash>
class Md4Hasher : public Hasher<Md4Hasher> class Md4Hasher : public Hasher<Md4Hasher>
{ {
public: public:
using HashType = Md4Hash;
static constexpr size_t BLOCK_SIZE_BYTES = 64;
Md4Hasher() Md4Hasher()
{ {
ResetImpl(); ResetImpl();
@@ -178,7 +182,7 @@ public:
MessageSizeBytes_ += UpdateImpl(begin, end); MessageSizeBytes_ += UpdateImpl(begin, end);
} }
Md4Hash Finish() HashType Finish()
{ {
uint64_t messageSizeBytesMod64 = MessageSizeBytes_ % 64; uint64_t messageSizeBytesMod64 = MessageSizeBytes_ % 64;
@@ -218,7 +222,7 @@ public:
UpdateImpl(encodedMessageSizeBits, UpdateImpl(encodedMessageSizeBits,
encodedMessageSizeBits + std::size(encodedMessageSizeBits)); encodedMessageSizeBits + std::size(encodedMessageSizeBits));
Md4Hash result; HashType result;
int_fast8_t i = 0; int_fast8_t i = 0;
for (int_fast8_t reg = 0; reg < 4; ++reg) for (int_fast8_t reg = 0; reg < 4; ++reg)

View File

@@ -186,6 +186,10 @@ struct Md5Hash : public Hash<Md5Hash>
class Md5Hasher : public Hasher<Md5Hasher> class Md5Hasher : public Hasher<Md5Hasher>
{ {
public: public:
using HashType = Md5Hash;
static constexpr size_t BLOCK_SIZE_BYTES = 64;
Md5Hasher() Md5Hasher()
{ {
ResetImpl(); ResetImpl();
@@ -202,7 +206,7 @@ public:
MessageSizeBytes_ += UpdateImpl(begin, end); MessageSizeBytes_ += UpdateImpl(begin, end);
} }
Md5Hash Finish() HashType Finish()
{ {
uint64_t messageSizeBytesMod64 = MessageSizeBytes_ % 64; uint64_t messageSizeBytesMod64 = MessageSizeBytes_ % 64;
@@ -242,7 +246,7 @@ public:
UpdateImpl(encodedMessageSizeBits, UpdateImpl(encodedMessageSizeBits,
encodedMessageSizeBits + std::size(encodedMessageSizeBits)); encodedMessageSizeBits + std::size(encodedMessageSizeBits));
Md5Hash result; HashType result;
int_fast8_t i = 0; int_fast8_t i = 0;
for (int_fast8_t reg = 0; reg < 4; ++reg) for (int_fast8_t reg = 0; reg < 4; ++reg)

115
Chaos/Mac/Hmac.hpp Normal file
View File

@@ -0,0 +1,115 @@
#ifndef CHAOS_MAC_HMAC_HPP
#define CHAOS_MAC_HMAC_HPP
#include <array>
#include <cstdint>
#include <type_traits>
#include "Hash/Hasher.hpp"
namespace Chaos::Mac::Hmac
{
template<typename HasherImpl,
typename = std::enable_if_t<std::is_base_of_v<Hash::Hasher<HasherImpl>, HasherImpl>>>
class Hmac
{
public:
template<typename InputIt>
Hmac(InputIt keyBegin, InputIt keyEnd)
{
Key_ = GenerateKey(keyBegin, keyEnd);
KeyType ipaddedKey = PadKey<IPAD_BYTE>(Key_);
Hasher_.Update(ipaddedKey.begin(), ipaddedKey.end());
}
template<typename InputIt>
void Update(InputIt begin, InputIt end)
{
Hasher_.Update(begin, end);
}
typename HasherImpl::HashType Finish()
{
auto innerDigest = Hasher_.Finish().GetRawDigest();
Hasher_.Reset();
KeyType opaddedKey = PadKey<OPAD_BYTE>(Key_);
Hasher_.Update(opaddedKey.begin(), opaddedKey.end());
Hasher_.Update(innerDigest.begin(), innerDigest.end());
return Hasher_.Finish();
}
private:
using KeyType = std::array<uint8_t, HasherImpl::BLOCK_SIZE_BYTES>;
static constexpr uint8_t OPAD_BYTE = 0x5c;
static constexpr uint8_t IPAD_BYTE = 0x36;
KeyType Key_;
HasherImpl Hasher_;
template<typename InputIt>
static KeyType GenerateKey(InputIt keyBegin, InputIt keyEnd)
{
KeyType key;
key.fill(0);
InputIt keyIt = keyBegin;
uint64_t idx = 0;
for (; keyIt != keyEnd && idx < key.size();
++keyIt, ++idx)
{
key[idx] = *keyIt;
}
if (keyIt != keyEnd)
{
HasherImpl keyHasher;
keyHasher.Update(key.begin(), key.end());
keyHasher.Update(keyIt, keyEnd);
auto digest = keyHasher.Finish().GetRawDigest();
static_assert(digest.size() <= HasherImpl::BLOCK_SIZE_BYTES);
key.fill(0);
idx = 0;
for (auto it = digest.begin();
it != digest.end() && idx < key.size();
++it, ++idx)
{
key[idx] = *it;
}
}
return key;
}
template<uint8_t PAD_BYTE>
static KeyType PadKey(const KeyType & key)
{
static_assert(PAD_BYTE == IPAD_BYTE || PAD_BYTE == OPAD_BYTE);
KeyType paddedKey;
uint64_t idx = 0;
for (auto it = key.cbegin();
it != key.cend() && idx < paddedKey.size();
++it, ++idx)
{
paddedKey[idx] = (*it) ^ PAD_BYTE;
}
return paddedKey;
}
};
} // namespace Chaos::Mac::Hmac
#endif

View File

@@ -12,7 +12,8 @@ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest) FetchContent_MakeAvailable(googletest)
set(ChaosTests_SOURCE Md4HasherTests.cpp set(ChaosTests_SOURCE Md4HasherTests.cpp
Md5HasherTests.cpp) Md5HasherTests.cpp
HmacTests.cpp)
add_executable(ChaosTests ${ChaosTests_SOURCE}) add_executable(ChaosTests ${ChaosTests_SOURCE})
target_link_libraries(ChaosTests gtest gtest_main) target_link_libraries(ChaosTests gtest gtest_main)

52
ChaosTests/HmacTests.cpp Normal file
View File

@@ -0,0 +1,52 @@
#include <gtest/gtest.h>
#include "Hash/Md5.hpp"
#include "Mac/Hmac.hpp"
using namespace Chaos::Mac::Hmac;
using namespace Chaos::Hash::Md5;
TEST(HmacTests, RFCTest)
{
struct Helper
{
std::string operator()(const char * key, const char * data) const
{
Hmac<Md5Hasher> hmac(key, key + strlen(key));
hmac.Update(data, data + strlen(data));
return hmac.Finish().ToHexString();
}
};
Helper hmacMd5;
{
const char * key = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
const char * data = "Hi There";
ASSERT_EQ("9294727a3638bb1c13f48ef8158bfc9d", hmacMd5(key, data));
}
{
const char * key = "Jefe";
const char * data = "what do ya want for nothing?";
ASSERT_EQ("750c783e6ab0b503eaa86e310a5db738", hmacMd5(key, data));
}
{
uint8_t key[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa };
uint8_t data[] = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd };
Hmac<Md5Hasher> hmacMd5(key, key + std::size(key));
hmacMd5.Update(data, data + std::size(data));
ASSERT_EQ("56be34521d144c88dbb8c733f0e8b3f6", hmacMd5.Finish().ToHexString());
}
}