HMAC

Join provides HMAC (Hash‑based Message Authentication Code) support using OpenSSL. The Hmac class allows computing keyed message authentication codes to ensure both data integrity and authenticity.

Unlike plain digests, HMAC uses a secret key and is not vulnerable to length‑extension attacks, making it suitable for authentication and message verification.

HMAC features:

  • Multiple algorithms — MD5, SHA‑1, SHA‑2 family, SM3
  • Keyed security — protects against forgery
  • Simple API — static one‑shot helpers
  • Stream support — incremental / large data processing
  • Binary or hex output
  • OpenSSL‑backed implementation

Typical use cases:

  • API request signing
  • Message authentication
  • Token generation
  • Secure cookies
  • Webhooks verification

Supported algorithms

AlgorithmOutput SizeSecurity Status
HMAC‑MD5128 bits⚠️ Legacy only
HMAC‑SHA‑1160 bits⚠️ Deprecated
HMAC‑SHA‑224224 bits✅ Secure
HMAC‑SHA‑256256 bits✅ Secure
HMAC‑SHA‑384384 bits✅ Secure
HMAC‑SHA‑512512 bits✅ Secure
HMAC‑SM3256 bits✅ Secure (Chinese)

⚠️ Do not use HMAC‑MD5 or HMAC‑SHA‑1 for new designs.


Quick start

HMAC‑SHA‑256 (hex)

// libjoin.
#include <join/hmac.hpp>

// C++.
#include <iostream>

using namespace join;

std::string data = "Hello, World!";
std::string key  = "secret-key";

std::string mac = Hmac::sha256hex(data, key);

std::cout << mac << std::endl;

HMAC‑SHA‑256 (binary)

BytesArray mac = Hmac::sha256bin(data, key);

Static methods

Each algorithm provides binary and hexadecimal helpers, taking the message first and the key second.

HMAC‑MD5

BytesArray  bin = Hmac::md5bin(data, key);
std::string hex = Hmac::md5hex(data, key);

HMAC‑SHA‑1

BytesArray  bin = Hmac::sha1bin(data, key);
std::string hex = Hmac::sha1hex(data, key);

HMAC‑SHA‑224

BytesArray  bin = Hmac::sha224bin(data, key);
std::string hex = Hmac::sha224hex(data, key);

HMAC‑SHA‑256

BytesArray  bin = Hmac::sha256bin(data, key);
std::string hex = Hmac::sha256hex(data, key);

HMAC‑SHA‑384

BytesArray  bin = Hmac::sha384bin(data, key);
std::string hex = Hmac::sha384hex(data, key);

HMAC‑SHA‑512

BytesArray  bin = Hmac::sha512bin(data, key);
std::string hex = Hmac::sha512hex(data, key);

HMAC‑SM3

BytesArray  bin = Hmac::sm3bin(data, key);
std::string hex = Hmac::sm3hex(data, key);

Input types

The message can be a std::string, a BytesArray or a raw buffer. The key is always a std::string.

String input

std::string mac = Hmac::sha256hex("message", "key");

Byte array input

BytesArray data = {0x01, 0x02, 0x03};
std::string key = "secret-key";

BytesArray mac = Hmac::sha256bin(data, key);

Raw buffer input

const char* data = "Hello, World!";
std::string key  = "secret-key";

BytesArray mac = Hmac::sha256bin(data, strlen(data), key);

Stream‑based HMAC

Use the Hmac stream for incremental processing.

Hmac hmac(Digest::SHA256, key);

hmac << "chunk1";
hmac << "chunk2";
hmac << "chunk3";

BytesArray mac = hmac.finalize();
std::string hex = bin2hex(mac);

Error checking

Hmac hmac(Digest::SHA256, key);
hmac << data;

BytesArray mac = hmac.finalize();

if (hmac.fail())
{
    std::cerr << "HMAC computation failed" << std::endl;
}

Verification

Constant‑time comparison

bool verify(const BytesArray& a, const BytesArray& b)
{
    if (a.size() != b.size()) return false;

    uint8_t diff = 0;
    for (size_t i = 0; i < a.size(); ++i)
    {
        diff |= a[i] ^ b[i];
    }
    return diff == 0;
}

Verifying a message

BytesArray expected = Hmac::sha256bin(data, key);
BytesArray received = ...;

if (verify(expected, received))
{
    // valid
}

Usage examples

API request signing

std::string signRequest(const std::string& key, const std::string& payload)
{
    return Hmac::sha256hex(payload, key);
}

Webhook verification

bool verifyWebhook(const std::string& key,
                   const std::string& payload,
                   const std::string& signature)
{
    std::string computed = Hmac::sha256hex(payload, key);

    // constant-time comparison (see verify() above), never use ==
    return verify(BytesArray(computed.begin(), computed.end()),
                  BytesArray(signature.begin(), signature.end()));
}

Secure cookies

std::string makeCookie(const std::string& key,
                       const std::string& data)
{
    std::string mac = Hmac::sha256hex(data, key);
    return data + "." + mac;
}

Error handling

Invalid algorithm

With OpenSSL 3.0 or later, an unsupported algorithm is reported by the stream state: finalize() returns an empty result and fail() is set. With older OpenSSL versions, the constructor throws std::system_error (DigestErrc::InvalidAlgorithm).

try
{
    Hmac hmac(static_cast<Digest::Algorithm>(999), key);  // throws with OpenSSL < 3.0
    hmac << data;

    BytesArray mac = hmac.finalize();
    if (hmac.fail())                                       // OpenSSL >= 3.0
    {
        std::cerr << "HMAC computation failed" << std::endl;
    }
}
catch (const std::system_error& e)
{
    if (e.code() == DigestErrc::InvalidAlgorithm)
    {
        std::cerr << "Unsupported HMAC algorithm" << std::endl;
    }
}

Security notes

  • HMAC prevents length‑extension attacks
  • Security relies on key secrecy
  • Use random keys (≥ 256 bits recommended)
  • Always use constant‑time comparison
  • Prefer SHA‑256 or stronger
  • Do not reuse HMAC keys for encryption

Summary

FeatureSupported
HMAC‑MD5✅
HMAC‑SHA‑1✅
HMAC‑SHA‑224✅
HMAC‑SHA‑256✅
HMAC‑SHA‑384✅
HMAC‑SHA‑512✅
HMAC‑SM3✅
Binary output✅
Hex output✅
Stream processing✅
Keyed authentication✅
OpenSSL backend✅