hmac-cpp English README
Лёгкая библиотека C++11 для вычисления HMAC (SHA-1/SHA-256/SHA-512), вывода ключей (PBKDF2, HKDF) и одноразовых паролей (HOTP, TOTP). Включает упрощённые временные HMAC-токены для статeless-сценариев и совместима с MQL5.
- Совместимость с C++11
- HMAC на основе SHA1, SHA256, SHA512
- Вывод в бинарном или hex-формате
- PBKDF2 (RFC 8018) — вывод ключа из пароля
- HKDF (RFC 5869) — извлечение/расширение ключа
- OTP:
- HOTP (RFC 4226) — счётчик
- TOTP (RFC 6238) — время
- Временные HMAC-токены — облегчённые токены на основе HMAC(timestamp) (не TOTP/HOTP)
- Поддержка MQL5 — адаптированные SHA/HMAC для MetaTrader 5
- Экспортируемая цель пакета CMake:
hmac_cpp::hmac_cpp
CI охватывает Linux/Windows/macOS. Тестировалась с GCC, Clang и MSVC; требуется C++11.
- Следуем Semantic Versioning.
- MAJOR: изменения, ломающие заголовки или экспортируемые символы.
- MINOR: обратно совместимые добавления.
- PATCH: исправления ошибок и внутренние изменения.
Макросы версии находятся в <hmac_cpp/version.hpp>:
HMAC_CPP_VERSION_MAJOR, HMAC_CPP_VERSION_MINOR,
HMAC_CPP_VERSION_PATCH и HMAC_CPP_VERSION.
История — в CHANGELOG.md.
Примеры, тесты и бенчмарки по умолчанию отключены. Включаются опциями:
HMACCPP_BUILD_EXAMPLESHMACCPP_BUILD_TESTSHMACCPP_BUILD_BENCHHMACCPP_ENABLE_MLOCK
Библиотека по умолчанию собирается статически. Чтобы получить динамическую,
используйте -DHMACCPP_BUILD_SHARED=ON. Макрос HMAC_CPP_API пуст для статической
сборки и управляет экспортом/импортом символов в динамической.
HMACCPP_ENABLE_MLOCK включает попытку пиновать секретные буферы в RAM с помощью
mlock/VirtualLock. Отключите, если платформа не позволяет.
cmake -B build -DHMACCPP_BUILD_EXAMPLES=ON
cmake --build buildcmake --install build --prefix _install
# MSVC
cmake --install build --config Release --prefix _installСтруктура установки:
_install/
├─ include/hmac_cpp/...
└─ lib/
└─ libhmac_cpp.a
Файл hmac_cpp.pc устанавливается для pkg-config.
find_package(hmac_cpp CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE hmac_cpp::hmac_cpp)# подберите пути под свой префикс
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)Предусмотрены скрипты сборки для MinGW: build_*.bat.
Замечание по SHA-1: HMAC-SHA1 поддерживается для совместимости/OTP. Для новых проектов предпочтительны HMAC-SHA256/512.
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- По умолчанию возвращается hex. Для бинарного вывода используйте перегрузку со
std::vector<uint8_t>.
Сравнение в постоянное время — длины считаются публичными:
bool equal = (a.size() == b.size()) && hmac::constant_time_equal(a, b);(Опционально) Безопасная работа со строковыми ключами — при использовании secure_buffer:
#include <hmac_cpp/secure_buffer.hpp>
secure_buffer key(std::move(secret_string)); // обнуляет перемещённую строку
auto mac = hmac::get_hmac(key, payload, hmac::TypeHash::SHA256);Для дополнительной защиты в памяти можно использовать hmac_cpp::secret_string, который
обфусцирует данные и по возможности закрепляет их в 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 действует только внутри коллбэка
});
token.clear();std::vector<uint8_t> get_hmac(
const void* key_ptr, size_t key_len,
const void* msg_ptr, size_t msg_len,
TypeHash type);template<typename T>
std::vector<uint8_t> get_hmac(
const std::vector<T>& key,
const std::vector<T>& msg,
TypeHash type);
// T должен быть char или uint8_tВывод ключа из пароля.
#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Рекомендации
- Соль: 16–32 случайных байт (уникальна для каждого пароля). Храните рядом с шифротекстом.
- Итерации: подберите ~100–250 мс на целевой платформе (настольный ≈ 600k, ноутбук ≈ 300k, мобильный ≈ 150k).
- Длина ключа: 32 байта; PRF: HMAC-SHA256.
PBKDF2 в основном нагружает CPU; для пользовательских паролей по возможности предпочтительны KDF с высокой требовательностью к памяти, например Argon2 или scrypt.
Пример сериализации (бинарный):
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)
Смотрите example_pbkdf2.cpp для полного примера.
std::vector<uint8_t> ikm = {/* секретные данные */};
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 (обычный и URL-алфавит) и 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);OTP по RFC 4226/6238. Секреты должны быть случайными (не паролями). Если получаете Base32 (otpauth URI), декодируйте перед вызовом.
- HOTP — 6 цифр, SHA-1 (по умолчанию).
- TOTP — шаг 30 с, 6 цифр, SHA-1 (по умолчанию).
is_totp_token_validпроверяет ±1 шаг.
#include <hmac_cpp/hmac_utils.hpp>
std::string key = "12345678901234567890"; // сырые байты
uint64_t counter = 0;
int hotp = get_hotp_code(key, counter);
int totp = get_totp_code(key); // now()Пример проверки (вектор RFC 6238):
bool ok = hmac::is_totp_token_valid(94287082, key, /*time=*/59, /*step=*/30,
/*digits=*/8, hmac::TypeHash::SHA1);Простая stateless схема HMAC(timestamp) (не TOTP/HOTP):
- По умолчанию SHA256 (поддерживаются также SHA1/SHA512)
- Тег — полный HMAC в hex
- Токен действителен в предыдущем/текущем/следующем интервале (±
interval_sec) - Возможна привязка к отпечатку клиента (ID устройства и т.п.)
- Нет защиты от повторов внутри интервала — для критичных задач используйте TOTP/HOTP или серверный учёт nonce
std::string token = hmac::generate_time_token(secret_key, /*interval=*/60);
bool valid = hmac::is_token_valid(token, secret_key, 60);
// с отпечатком
std::string t2 = hmac::generate_time_token(secret_key, fingerprint, 60);
bool v2 = hmac::is_token_valid(t2, secret_key, fingerprint, 60);hmac_cpp::encoding предоставляет простые преобразования:
- Base64 — стандартный
+/и URL-безопасный-_алфавиты;pad=true/falseвключает или отключает=.strict=trueотклоняет пробелы, смешанный паддинг и+//при URL-алфавите;strict=falseигнорирует ASCII-пробелы, допускает эти символы и добавляет недостающий паддинг. - Base32 —
pad=true/falseуправляет=;strict=true/falseработает аналогично. - Base36 — кодирует сырые байты в ASCII-цифры/буквы; при декодировании требуется полный ввод.
Репозиторий предоставляет sha256.mqh, sha512.mqh, hmac.mqh, hmac_utils.mqh (MetaTrader 5).
Установка: скопируйте файлы в каталог MT5, например MQL5/Include/hmac-cpp/, затем:
#include <hmac-cpp/hmac.mqh>
string mac = hmac::get_hmac("key", "message", hmac::TypeHash::SHA256);| Хеш-функция | C++ enum | MQL enum |
|---|---|---|
| SHA1 | hmac::TypeHash::SHA1 |
– (недоступно) |
| SHA256 | hmac::TypeHash::SHA256 |
hmac::TypeHash::SHA256 |
| SHA512 | hmac::TypeHash::SHA512 |
hmac::TypeHash::SHA512 |
Примечание: в C++ используется
hmac_cpp/...(подчёркивание), в MQL —hmac-cpp/...(дефис).
Включите тесты и запустите их через CTest:
cmake -B build -DHMACCPP_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build --output-on-failureПокрытые векторы:
- HMAC — RFC 4231
- PBKDF2 — RFC 6070
- HOTP — RFC 4226 (Appendix D)
- TOTP — RFC 6238 (Appendix B)
CI запускает их на Linux/Windows/macOS.
example.cpp собирается при 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";
}Ручная компиляция после установки:
g++ example.cpp -std=c++11 -I_install/include -L_install/lib -lhmac_cppMSVC:
cl /EHsc example.cpp /I _install\\include /link /LIBPATH:_install\\lib hmac_cpp.libsecure_buffer очищает память при разрушении и обнуляет усечённый хвост при
изменении размера. Он не закрепляет страницы в RAM, не предоставляет защиту
страниц и не предотвращает атаки соседних буферов.
pbkdf2,hkdf_*, HOTP/TOTP и временные токены проверяют параметры и бросаютstd::invalid_argument; функции временных токенов также могут бросатьstd::runtime_error, если системные часы недоступны.base64_decodeиbase32_decodeпомеченыnoexceptи возвращаютfalseпри некорректном вводе.constant_time_equal—noexcept; перед сравнением проверьте совпадение размеров.- Ограничения PBKDF2:
dkLen ≤ (2^32−1)·hLen; итераций ≥ 1; рекомендуемая длина соли ≥ 16 байт. - Ограничения HKDF:
L ≤ 255·HashLen. - Потокобезопасность: функции не имеют состояния и потокобезопасны при раздельных буферах.
- Оригинальный SHA-256: http://www.zedwood.com/article/cpp-sha256-function
- Оригинальный SHA-512: http://www.zedwood.com/article/cpp-sha512-function
- HMAC (wiki): https://en.wikipedia.org/wiki/HMAC
MIT — см. LICENSE.