Skip to content

Navigation Menu

Sign in
Sign up

Latest commit

History

169 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Linux Windows macOS License: MIT

A lightweight C++11 library for computing HMAC (SHA-1/SHA-256/SHA-512), key derivation (PBKDF2, HKDF), and OTP (HOTP, TOTP). Includes optional time-based HMAC tokens for simple stateless use-cases and MQL5 compatibility.


πŸš€ Features

  • C++11 compatible

  • HMAC with SHA1, SHA256, SHA512

  • Output as binary or hex

  • PBKDF2 (RFC 8018) β€” password-based key derivation

  • HKDF (RFC 5869) β€” key extraction/expansion

  • OTP:

    • HOTP (RFC 4226) β€” counter-based
    • TOTP (RFC 6238) β€” time-based
  • Time-based HMAC tokens β€” lightweight, stateless HMAC(timestamp)-style tokens (not TOTP/HOTP)

  • MQL5 support β€” adapted SHA/HMAC for MetaTrader 5

  • Exported CMake package target: hmac_cpp::hmac_cpp


βš™οΈ Platform & Compiler Support

CI covers Linux/Windows/macOS. Tested with GCC, Clang, and MSVC; requires C++11.


πŸ“ˆ Versioning / SemVer policy

  • Follows Semantic Versioning.
  • Before 1.0, a MINOR release may include breaking changes to headers or exported symbols; CMake package discovery therefore requires an exact version.
  • After 1.0, MAJOR denotes breaking changes and MINOR denotes backward-compatible additions.
  • PATCH: bug fixes and internal changes.

Version macros live in <hmac_cpp/version.hpp>: HMAC_CPP_VERSION_MAJOR, HMAC_CPP_VERSION_MINOR, HMAC_CPP_VERSION_PATCH, and HMAC_CPP_VERSION. See CHANGELOG.md for history.


πŸ”§ Build & Installation

Examples, tests, and benchmarks are OFF by default. Enable via:

  • HMACCPP_BUILD_EXAMPLES
  • HMACCPP_BUILD_TESTS
  • HMACCPP_BUILD_BENCH
  • HMACCPP_ENABLE_MLOCK

The library builds static by default. Use -DHMACCPP_BUILD_SHARED=ON to produce a shared library. The HMAC_CPP_API macro is empty for static builds and controls symbol export/import for shared builds.

HMACCPP_ENABLE_MLOCK toggles best-effort page locking via mlock/VirtualLock. Disable it if the platform lacks the necessary privileges.

Page-locking limitations

secure_buffer<T, true> attempts page locking only when HMACCPP_ENABLE_MLOCK is enabled; it is not page-locked by default. Lock ownership is coordinated only inside one loaded copy of hmac-cpp. Statically linking separate copies into multiple modules creates separate registries.

On POSIX, continuing in a child process after fork() without exec() is not supported for page locking: the child must immediately exec() or avoid using the inherited secure buffers.

Build

cmake -B build -DHMACCPP_BUILD_EXAMPLES=ON
cmake --build build

Install

cmake --install build --prefix _install
# MSVC
cmake --install build --config Release --prefix _install

Install layout:

_install/
β”œβ”€β”€ include/hmac_cpp/...
└── lib/
 └── libhmac_cpp.a

A hmac_cpp.pc file is installed for pkg-config.

Consume with CMake

find_package(hmac_cpp CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE hmac_cpp::hmac_cpp)

Manual compile after install

# adjust paths to your prefix
g++ example.cpp -std=c++11 -I_install/include -L_install/lib -lhmac_cpp
# MSVC
cl /EHsc example.cpp /I _install\include /link /LIBPATH:_install\lib hmac_cpp.lib
# pkg-config
c++ example.cpp $(pkg-config --cflags --libs hmac_cpp)

Predefined MinGW build scripts are available: build_*.bat.


πŸ“˜ Usage

Note on SHA-1: HMAC-SHA1 is supported for compatibility/OTP. Prefer HMAC-SHA256/512 for new designs.

HMAC (string input)

std::string get_hmac(
 const std::string& key,
 const std::string& msg,
 TypeHash type,
 bool is_hex = true,
 bool is_upper = false);
  • type: hmac::TypeHash::SHA256 / SHA512 / SHA1
  • Returns hex by default. For binary output, prefer the std::vector<uint8_t> overload.

Constant-time compare β€” treat lengths as public:

bool equal = (a.size() == b.size()) && hmac::constant_time_equal(a, b);

(Optional) Secure handling of string keys β€” if you use secure_buffer:

#include <hmac_cpp/secure_buffer.hpp>
secure_buffer key(std::move(secret_string)); // zeroizes moved-from string
auto mac = hmac::get_hmac(key, payload, hmac::TypeHash::SHA256);

For additional protection in memory, hmac_cpp::secret_string obfuscates the data and tries to keep it locked in RAM:

#include <hmac_cpp/secret_string.hpp>
hmac_cpp::secret_string token("super-secret-token");
token.with_plaintext([](const uint8_t* p, size_t n){
 // p is valid only inside the callback
});
token.clear();

HMAC (raw buffer)

std::vector<uint8_t> get_hmac(
 const void* key_ptr, size_t key_len,
 const void* msg_ptr, size_t msg_len,
 TypeHash type);

HMAC (vectors)

template<typename T>
std::vector<uint8_t> get_hmac(
 const std::vector<T>& key,
 const std::vector<T>& msg,
 TypeHash type);
// T must be char or uint8_t

PBKDF2 (RFC 8018)

Derive a key from a password.

#include <hmac_cpp/hmac_utils.hpp>
auto salt = hmac::random_bytes(16);
auto key = hmac::pbkdf2_hmac_sha256(password, salt, iters, 32); // 32 = AES-256

Recommendations

  • Salt: 16–32 random bytes (unique per password). Store next to ciphertext.
  • Iterations: tune for ~100–250 ms on target hardware (e.g., desktop β‰ˆ 600k, laptop β‰ˆ 300k, mobile β‰ˆ 150k; adjust).
  • Derived key length: 32 bytes; PRF: HMAC-SHA256.

PBKDF2 is CPU-bound; for user passwords prefer memory-hard KDFs such as Argon2 or scrypt if available.

Serialization example (binary):

magic(4) | ver(1) | alg(1=PBKDF2-HS256) |
iter(4, BE) | salt_len(1) | salt | iv_len(1) | iv | ct_len(4, BE) | ct | tag(16)

See example_pbkdf2.cpp for an end-to-end example.

HKDF (RFC 5869)

std::vector<uint8_t> ikm = {/* secret material */};
std::vector<uint8_t> salt(16, 0x00);
auto prk = hmac::hkdf_extract_sha256(ikm, salt);
auto okm = hmac::hkdf_expand_sha256(prk, /*info=*/{}, /*L=*/32); // L ≀ 255*HashLen

Base64 / Base32

Utility helpers for Base64 (standard or URL alphabet) and Base32.

#include <hmac_cpp/encoding.hpp>
std::vector<uint8_t> key = {0xff, 0xee};
std::string b64 = hmac_cpp::base64_encode(key, hmac_cpp::Base64Alphabet::Url, false);
hmac_cpp::secure_buffer<uint8_t> raw;
hmac_cpp::base64_decode(b64, raw, hmac_cpp::Base64Alphabet::Url, false);

πŸ•“ HOTP / TOTP

OTP per RFC 4226/6238. Secrets should be random (not passwords). If you receive Base32 (otpauth URI), decode before calling.

  • HOTP β€” 6 digits, SHA-1 (default).
  • TOTP β€” 30 s step, 6 digits, SHA-1 (default). is_totp_token_valid checks Β±1 step by default.
#include <hmac_cpp/hmac_utils.hpp>
std::string key = "12345678901234567890"; // raw bytes
uint64_t counter = 0;
int hotp = get_hotp_code(key, counter);
int totp = get_totp_code(key); // now()

Validation example (RFC 6238 vector):

bool ok = hmac::is_totp_token_valid(94287082, key, /*time=*/59, /*step=*/30,
 /*digits=*/8, hmac::TypeHash::SHA1);

πŸ•“ Time-Based HMAC Tokens (custom)

A simple stateless HMAC(timestamp) scheme (not TOTP/HOTP):

  • Default SHA256 (also SHA1/SHA512 supported)
  • Full HMAC digest as tag (hex)
  • Valid within previous/current/next interval (Β±interval_sec)
  • Optional binding to a client fingerprint (device ID, etc.)
  • No replay protection within the interval β€” use TOTP/HOTP or server-side nonce tracking for high-risk scenarios
std::string token = hmac::generate_time_token(secret_key, /*interval=*/60);
bool valid = hmac::is_token_valid(token, secret_key, 60);
// with fingerprint
std::string t2 = hmac::generate_time_token(secret_key, fingerprint, 60);
bool v2 = hmac::is_token_valid(t2, secret_key, fingerprint, 60);

Encoding utilities

hmac_cpp::encoding provides simple conversions:

  • Base64 (RFC 4648)β€”standard +/ and URL-safe -_ alphabets; pad=true/false toggles = padding. strict=true rejects whitespace, mixed padding and +// when using the URL alphabet; strict=false ignores ASCII spaces, accepts these aliases and tolerates missing padding.
  • Base32 (RFC 4648)β€”alphabet A–Z2–7; encoder emits upper-case. Decoder with strict=true requires = padding and upper-case; with strict=false it tolerates lower-case and whitespace.
  • Base36β€”fixed 2-char/byte scheme using 0–9A–Z; not a cryptographic format. Leading zero bytes are preserved (e.g. {0,0,1} β†’ "001").

Example

std::string b64 = hmac_cpp::base64_encode(buf.data(), buf.size(),
 hmac_cpp::Base64Alphabet::Url, /*pad=*/false);
hmac_cpp::base64_decode(b64, raw, hmac_cpp::Base64Alphabet::Url,
 /*require_padding=*/false, /*strict=*/true);
std::string b32 = hmac_cpp::base32_encode(buf.data(), buf.size(), /*pad=*/true);
hmac_cpp::base32_decode(b32, raw, /*require_padding=*/true, /*strict=*/false);
std::string b36 = hmac_cpp::base36_encode(buf.data(), buf.size());
hmac_cpp::base36_decode(b36, raw);

πŸ“¦ MQL5 Compatibility

Repository provides sha256.mqh, sha512.mqh, hmac.mqh, hmac_utils.mqh (MetaTrader 5).

Install: copy these files into your MT5 folder, e.g. MQL5/Include/hmac-cpp/, then:

#include <hmac-cpp/hmac.mqh>
string mac = hmac::get_hmac("key", "message", hmac::TypeHash::SHA256);
Hash function C++ enum MQL enum
SHA1 hmac::TypeHash::SHA1 – (not available)
SHA256 hmac::TypeHash::SHA256 hmac::TypeHash::SHA256
SHA512 hmac::TypeHash::SHA512 hmac::TypeHash::SHA512

Note: C++ includes use hmac_cpp/... (underscore), MQL includes use hmac-cpp/... (dash directory name).


βœ… Tests & Vectors

Enable tests and run with CTest:

cmake -B build -DHMACCPP_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build --output-on-failure

Covered vectors:

Suite RFC
HMAC RFC 4231
PBKDF2 RFC 6070
HOTP RFC 4226 (App D)
TOTP RFC 6238 (App B)

CI: GitHub Actions.


πŸ“„ Example Program

example.cpp builds when HMACCPP_BUILD_EXAMPLES=ON.

#include <iostream>
#include <hmac_cpp/hmac.hpp>
#include <hmac_cpp/hmac_utils.hpp>
int main() {
 std::string input = "grape";
 std::string key = "12345";
 std::string mac = hmac::get_hmac(key, input, hmac::TypeHash::SHA256);
 bool ok = (mac.size() == 64) &&
 hmac::constant_time_equal(
 mac,
 "7632ac2e8ddedaf4b3e7ab195fefd17571c37c970e02e169195a158ef59e53ca");
 if (ok) std::cout << "MAC verified\n";
}

Manual build after install:

g++ example.cpp -std=c++11 -I_install/include -L_install/lib -lhmac_cpp

MSVC:

cl /EHsc example.cpp /I _install\include /link /LIBPATH:_install\lib hmac_cpp.lib

πŸ”’ Security notes

secure_buffer wipes its memory on destruction and zeroizes truncated bytes on resize. It does not page‐lock buffers, provide guard pages, or mitigate neighboring memory attacks.


⚠️ Exceptions & Contracts

  • pbkdf2, hkdf_*, HOTP/TOTP, and time-token helpers validate parameters and throw std::invalid_argument; time-token helpers also throw std::runtime_error if the system clock fails.
  • base64_decode and base32_decode are noexcept and return false on invalid input.
  • constant_time_equal is noexcept; compare sizes first.
  • PBKDF2 limits: dkLen ≀ (2^32βˆ’1)Β·hLen; iterations β‰₯ 1; salt length β‰₯ 16 recommended.
  • HKDF limits: L ≀ 255Β·HashLen.
  • Thread-safety: functions are stateless and thread-safe given separate buffers.

πŸ“š Resources


πŸ”— Related Projects


πŸ“ License

MIT β€” see LICENSE.

About

C++11 HMAC (SHA1/SHA256/SHA512) library with PBKDF2/HKDF, HOTP/TOTP + stateless time tokens, Base32/64/36, zeroizing secure_buffer, CMake package and MQL5 support.

Topics

Resources

Stars

11 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /