diff --git a/README-RU.md b/README-RU.md index 54752f4..8389087 100644 --- a/README-RU.md +++ b/README-RU.md @@ -1,84 +1,95 @@ -# hmac-cpp +# hmac-cpp [English README](./README.md) [![Linux](https://github.com/NewYaroslav/hmac-cpp/actions/workflows/CI-Linux.yml/badge.svg?branch=main)](https://github.com/NewYaroslav/hmac-cpp/actions/workflows/CI-Linux.yml) [![Windows](https://github.com/NewYaroslav/hmac-cpp/actions/workflows/CI-Win.yml/badge.svg?branch=main)](https://github.com/NewYaroslav/hmac-cpp/actions/workflows/CI-Win.yml) [![macOS](https://github.com/NewYaroslav/hmac-cpp/actions/workflows/CI-macOS.yml/badge.svg?branch=main)](https://github.com/NewYaroslav/hmac-cpp/actions/workflows/CI-macOS.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) -Лёгкая `C++11` библиотека для вычисления `HMAC` (hash-based message authentication code), поддерживающая поддерживающая `SHA256`, `SHA512`, `SHA1`, а также одноразовые пароли `HOTP` и `TOTP`. +Лёгкая библиотека **C++11** для вычисления **HMAC** (SHA-1/SHA-256/SHA-512), вывода ключей (**PBKDF2**, **HKDF**) и одноразовых паролей (**HOTP**, **TOTP**). Включает упрощённые **временные HMAC-токены** для статeless-сценариев и совместима с **MQL5**. + +--- ## 🚀 Возможности -- Совместимость с **C++11** -- Поддержка `HMAC` на основе `SHA256`, `SHA512`, `SHA1` -- Прямая работа с бинарным или hex-форматом -- Поддержка **PBKDF2** (RFC 8018) -- Поддержка **HKDF** (RFC 5869) для извлечения и расширения ключей -- Поддержка **временных токенов**: - - **HOTP (RFC 4226)** — счётчики - - **TOTP (RFC 6238)** — временные токены - - **HMAC Time Tokens** — облегчённые временные токены с HMAC-подписью и интервалом -- Поддержка **MQL5** — адаптированные версии SHA/HMAC для MetaTrader -- Статическая сборка через CMake -- Пример использования в комплекте +* Совместимость с **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. + +--- -## 🔧 Установка и сборка +## 🔧 Сборка и установка -По умолчанию примеры, тесты и бенчмарки не собираются. Включите их с помощью -`HMACCPP_BUILD_EXAMPLES`, `HMACCPP_BUILD_TESTS` и `HMACCPP_BUILD_BENCH`. +Примеры, тесты и бенчмарки по умолчанию отключены. Включаются опциями: -Для сборки используйте CMake: +* `HMACCPP_BUILD_EXAMPLES` +* `HMACCPP_BUILD_TESTS` +* `HMACCPP_BUILD_BENCH` + +### Сборка ```bash cmake -B build -DHMACCPP_BUILD_EXAMPLES=ON cmake --build build ``` -Чтобы установить библиотеку и заголовки: +### Установка ```bash cmake --install build --prefix _install ``` -Это создаст структуру: +Структура установки: ``` _install/ -├── include/hmac_cpp/ -│ ├── hmac.hpp -│ ├── hmac_utils.hpp -│ ├── sha1.hpp -│ ├── sha256.hpp -│ └── sha512.hpp -└── lib/ - └── libhmac.a +├─ include/hmac_cpp/ +│ ├─ hmac.hpp +│ ├─ hmac_utils.hpp +│ ├─ sha1.hpp +│ ├─ sha256.hpp +│ ├─ sha512.hpp +│ └─ secure_buffer.hpp # если включён в сборку +└─ lib/ + └─ libhmac_cpp.a ``` -Подключайте заголовки как `` +### Использование с CMake -Также в репозитории есть готовые `.bat`-файлы для сборки под MinGW: `build_*.bat`. +```cmake +find_package(hmac_cpp CONFIG REQUIRED) +target_link_libraries(my_app PRIVATE hmac_cpp::hmac_cpp) +``` -## 📦 MQL5-совместимость +### Ручная компиляция после установки -В репозитиории содержатся файлы `sha256.mqh`, `sha512.mqh`, `hmac.mqh`, `hmac_utils.mqh`, полностью совместимые с `MetaTrader 5`. +```bash +# подберите пути под свой префикс +g++ example.cpp -std=c++11 -I_install/include -L_install/lib -lhmac_cpp +``` -Вы можете использовать аналогичные вызовы в скриптах и советниках: +Предусмотрены скрипты сборки для MinGW: `build_*.bat`. -```mql5 -#include - -string hash = hmac::get_hmac("key", "message", hmac::TypeHash::SHA256); -``` +--- -| Хеш-функция | Значение в C++ | Значение в MQL | -|-------------|--------------------------|--------------------------| -| SHA1 | `hmac::TypeHash::SHA1` | – (не доступно) | -| SHA256 | `hmac::TypeHash::SHA256` | `hmac::TypeHash::SHA256` | -| SHA512 | `hmac::TypeHash::SHA512` | `hmac::TypeHash::SHA512` | +## 📘 Использование -## Использование +> **Замечание по SHA-1**: HMAC-SHA1 поддерживается для совместимости/OTP. Для новых проектов предпочтительны HMAC-SHA256/512. -### HMAC (ввод в виде строки) +### HMAC (строковый ввод) ```cpp std::string get_hmac( @@ -89,210 +100,146 @@ std::string get_hmac( bool is_upper = false); ``` -Параметры: +* `type`: `hmac::TypeHash::SHA256` / `SHA512` / `SHA1` +* По умолчанию возвращается hex. Для **бинарного** вывода используйте перегрузку со `std::vector`. -- `key` — Секретный ключ -- `msg` — Сообщение -- `type` — Тип хеш-функции: `hmac::TypeHash::SHA256` или `SHA512` -- `is_hex` — Возвращать hex-строку (`true`) или бинарные данные (`false`) [по умолчанию: true] -- `is_upper` — Использовать верхний регистр (только для `hex`) [по умолчанию: false] - -Возвращает: -Если `is_hex == true`, возвращает HMAC в виде hex-строки (`std::string`). -Если `is_hex == false`, возвращает бинарную строку; для бинарного вывода предпочтительнее перегрузка со `std::vector`. - -#### Безопасная работа со строковыми ключами - -Если секретный ключ получен в виде `std::string` (например, API‑ключ биржи), -переместите его в `secure_buffer`, чтобы исходная строка сразу очистилась: +**Сравнение в постоянное время** — длины считаются публичными: ```cpp -#include -#include - -std::string api_key = std::getenv("API_KEY"); -secure_buffer key(std::move(api_key)); // api_key очищена - -auto sig = hmac::get_hmac(key, payload, hmac::TypeHash::SHA256); -secure_zero(key); // при необходимости: очистить после использования -``` - -Чтобы сравнить два токена напрямую, используйте -`hmac::constant_time_equal` для защиты от атак по времени: - -```cpp -bool same = hmac::constant_time_equal(expected_token, user_token); // длины публичны -``` - -### HMAC (сырые бинарные данные) - -```cpp -std::vector get_hmac( - const void* key_ptr, - size_t key_len, - const void* msg_ptr, - size_t msg_len, - TypeHash type -); +bool equal = (a.size() == b.size()) && hmac::constant_time_equal(a, b); ``` -Параметры: - -- `key_ptr` — Указатель на буфер с ключом -- `key_len` — Размер ключа в байтах -- `msg_ptr` — Указатель на буфер с сообщением -- `msg_len` — Размер сообщения в байтах -- `type` — Тип хеш-функции - -Возвращает: Бинарный HMAC в виде `std::vector` - -### HMAC (векторы) +**(Опционально) Безопасная работа со строковыми ключами** — при использовании `secure_buffer`: ```cpp -template -std::vector get_hmac( - const std::vector& key, - const std::vector& msg, - TypeHash type -); +#include +secure_buffer key(std::move(secret_string)); // обнуляет перемещённую строку +auto mac = hmac::get_hmac(key, payload, hmac::TypeHash::SHA256); ``` -Требования шаблона: `T` должен быть `char` или `uint8_t` - -Параметры: - -- `key` — Вектор, содержащий ключ -- `msg` — Вектор, содержащий сообщение -- `type` — Тип хеш-функции - -Возвращает: Бинарный HMAC в виде `std::vector` - -### PBKDF2 +### PBKDF2 (RFC 8018) -PBKDF2 преобразует пароль пользователя в криптографический ключ. -Используется для шифрования и хранения хешей паролей. +Вывод ключа из пароля. ```cpp #include auto salt = hmac::random_bytes(16); -auto key = hmac::pbkdf2_hmac_sha256(password, salt, iters, 32); +auto key = hmac::pbkdf2_hmac_sha256(password, salt, iters, 32); // 32 = AES-256 ``` -Рекомендации: +**Рекомендации** -- **Соль:** случайные 16–32 байта. -- **Итерации:** подбирайте число так, чтобы вычисление занимало ~100–250 мс на целевой машине. -- **Длина ключа:** 32 байта. -- **Алгоритм:** HMAC-SHA-256. +* **Соль**: 16–32 случайных байт (уникальна для каждого пароля). Храните рядом с шифротекстом. +* **Итерации**: подберите ~100–250 мс на целевой платформе (настольный ≈ 600k, ноутбук ≈ 300k, мобильный ≈ 150k). +* **Длина ключа**: 32 байта; **PRF**: HMAC-SHA256. -Параметры и шифротекст можно сериализовать, например, так: -`magic|salt|iters|iv|ct|tag`. +**Пример сериализации** (бинарный): -См. `example_pbkdf2.cpp` для полноценного примера. - -#### Рекомендуемые параметры - -| Цель | Итерации | Длина ключа | PRF | -|--------|---------:|------------:|-------------| -| Desktop| 600000 | 32 байта | HMAC-SHA256 | -| Laptop | 300000 | 32 байта | HMAC-SHA256 | -| Mobile | 150000 | 32 байта | HMAC-SHA256 | +``` +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` для полного примера. -- PBKDF2 зависит от CPU и уязвим для атак с использованием GPU/ASIC, поэтому выбирайте высокое число итераций или более сильные KDF. -- Каждому паролю нужна уникальная случайная соль достаточной длины. -- Соль, итерации и алгоритм не являются секретом — храните их вместе с хешем или шифротекстом. +### HKDF (RFC 5869) -### 🕓 HOTP и TOTP токены +```cpp +std::vector ikm = {/* секретные данные */}; +std::vector salt(16, 0x00); +auto prk = hmac::hkdf_extract_sha256(ikm, salt); +auto okm = hmac::hkdf_expand_sha256(prk, /*info=*/{}, /*L=*/32); // L ≤ 255*HashLen +``` -Библиотека поддерживает генерацию одноразовых паролей по RFC 4226 и RFC 6238. -Секрет передаётся в виде сырых байт. Если он задан в Base32 (часто в OTP URI), -сначала декодируйте его. +### 🕓 HOTP / TOTP -- **HOTP** — 6 цифр, SHA-1. -- **TOTP** — период 30 с, 6 цифр, SHA-1. `is_totp_token_valid` допускает окно ±1 интервал. +OTP по RFC 4226/6238. **Секреты должны быть случайными** (не паролями). Если получаете Base32 (otpauth URI), декодируйте перед вызовом. -#### HOTP (HMAC-based One-Time Password) +* **HOTP** — 6 цифр, SHA-1 (по умолчанию). +* **TOTP** — шаг 30 с, 6 цифр, SHA-1 (по умолчанию). `is_totp_token_valid` проверяет ±1 шаг. ```cpp #include -std::string key = "12345678901234567890"; // raw key +std::string key = "12345678901234567890"; // сырые байты uint64_t counter = 0; -int otp = get_hotp_code(key, counter); // по умолчанию: 6 цифр, SHA1 -std::cout << "HOTP: " << otp << std::endl; -bool ok = (otp == 755224); // тестовый вектор RFC 4226 +int hotp = get_hotp_code(key, counter); +int totp = get_totp_code(key); // now() ``` -#### TOTP (Time-based One-Time Password) +Пример проверки (вектор RFC 6238): +```cpp +bool ok = hmac::is_totp_token_valid(94287082, key, /*time=*/59, /*step=*/30, + /*digits=*/8, hmac::TypeHash::SHA1); ``` -#include -std::string key = "12345678901234567890"; // raw key -int otp = get_totp_code(key); // по умолчанию: 30 сек, 6 цифр, SHA1 -std::cout << "TOTP: " << otp << std::endl; -``` +### 🕓 Временные токены на основе HMAC (кастомные) -Можно задать конкретную метку времени: +Простая **stateless** схема `HMAC(timestamp)` *(не TOTP/HOTP)*: + +* По умолчанию **SHA256** (поддерживаются также SHA1/SHA512) +* Тег — полный HMAC в hex +* Токен действителен в предыдущем/текущем/следующем интервале (±`interval_sec`) +* Возможна привязка к **отпечатку клиента** (ID устройства и т.п.) +* **Нет защиты от повторов** внутри интервала — для критичных задач используйте TOTP/HOTP или серверный учёт nonce ```cpp -uint64_t time_at = 1700000000; -int otp = get_totp_code_at(key, time_at); +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); ``` -Для проверки кода: +--- -```cpp -bool valid = hmac::is_totp_token_valid(94287082, key, 59, 30, 8, hmac::TypeHash::SHA1); // тестовый вектор RFC 6238 -``` +## 📦 Совместимость с MQL5 -Известные тестовые векторы: [RFC 4226, приложение D](https://www.rfc-editor.org/rfc/rfc4226#appendix-D) и [RFC 6238, приложение B](https://www.rfc-editor.org/rfc/rfc6238#appendix-B). +Репозиторий предоставляет `sha256.mqh`, `sha512.mqh`, `hmac.mqh`, `hmac_utils.mqh` (MetaTrader 5). -### 🕓 Временные токены на основе HMAC (Custom HMAC Time Tokens) +**Установка:** скопируйте файлы в каталог MT5, например `MQL5/Include/hmac-cpp/`, затем: -Библиотека также включает **облегчённую реализацию временных HMAC-токенов**. Это **не** TOTP/HOTP; используется простой механизм `HMAC(timestamp)`. Эти токены: +```mql5 +#include +string mac = hmac::get_hmac("key", "message", hmac::TypeHash::SHA256); +``` -- Основаны на `HMAC(timestamp)` — не TOTP/HOTP -- По умолчанию применяется `SHA256` (поддерживаются также `SHA1` и `SHA512`) -- Тег — полный HMAC: 32 байта (64 hex-символа) при `SHA256` -- Кодирование: `hex` в нижнем регистре -- Токен принимается для предыдущего, текущего и следующего интервала (±`interval_sec`) -- Не требуют хранения состояния и могут привязываться к *отпечатку клиента* (например, ID устройства) -- Обеспечивают базовую защиту от повторного воспроизведения и подходят только для задач с низким риском +| Хеш-функция | 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/...` (дефис). -```cpp -#include +--- -std::string token = hmac::generate_time_token(secret_key, 60); -bool is_valid = hmac::is_token_valid(token, secret_key, 60); -``` +## ✅ Тесты и векторы -Также можно привязать токен к *отпечатку клиента* (fingerprint): +Включите тесты и запустите их через CTest: -```cpp -std::string token = hmac::generate_time_token(secret_key, fingerprint, 60); -bool is_valid = hmac::is_token_valid(token, secret_key, fingerprint, 60); +```bash +cmake -B build -DHMACCPP_BUILD_TESTS=ON +cmake --build build +ctest --test-dir build --output-on-failure ``` -Если `interval_sec` неположителен, функции выбросят `std::invalid_argument`: +Покрытые векторы: -```cpp -try { - hmac::generate_time_token(secret_key, 0); -} catch (const std::invalid_argument& e) { - std::cout << e.what(); -} -``` +* HMAC — **RFC 4231** +* PBKDF2 — **RFC 6070** +* HOTP — **RFC 4226** (Appendix D) +* TOTP — **RFC 6238** (Appendix B) -Подходит для авторизации, защиты API и одноразовых токенов. +CI запускает их на Linux/Windows/macOS. -## 📄 Пример +--- -Пример находится в `example.cpp` и собирается при `HMACCPP_BUILD_EXAMPLES=ON`. +## 📄 Пример программы + +`example.cpp` собирается при `HMACCPP_BUILD_EXAMPLES=ON`. ```cpp #include @@ -304,48 +251,56 @@ int main() { std::string key = "12345"; std::string mac = hmac::get_hmac(key, input, hmac::TypeHash::SHA256); - if (hmac::constant_time_equal(mac, - "7632ac2e8ddedaf4b3e7ab195fefd17571c37c970e02e169195a158ef59e53ca")) { - std::cout << "MAC проверен\n"; - } - - return 0; + bool ok = (mac.size() == 64) && + hmac::constant_time_equal( + mac, + "7632ac2e8ddedaf4b3e7ab195fefd17571c37c970e02e169195a158ef59e53ca"); + if (ok) std::cout << "MAC verified\n"; } ``` -**Примечание:** `constant_time_equal` считает длину входных данных публичной и -время работы зависит от максимальной длины. Не проверяйте длины отдельно — -ранние проверки могут выдать информацию через побочные каналы времени -выполнения. - -Скомпилировать пример вручную после установки: +Ручная компиляция после установки: ```bash -g++ example.cpp -std=c++11 -Iinclude -Llib -lhmac_cpp +g++ example.cpp -std=c++11 -I_install/include -L_install/lib -lhmac_cpp ``` -Для MSVC: +MSVC: ```bat -cl /EHsc example.cpp /I include /link libhmac_cpp.lib +cl /EHsc example.cpp /I _install\\include /link /LIBPATH:_install\\lib hmach_cpp.lib ``` -## 📚 Полезные ссылки +--- + +## ⚠️ Исключения и контракты + +* Функции могут бросать `std::invalid_argument` (неверные параметры) и `std::runtime_error` (внутренние ошибки). +* `constant_time_equal` предполагает публичность длин; сравнивайте размеры заранее. +* Ограничения PBKDF2: `dkLen ≤ (2^32−1)·hLen`; итераций ≥ 1; рекомендуемая длина соли ≥ 16 байт. +* Ограничения HKDF: `L ≤ 255·HashLen`. +* Потокобезопасность: функции статичны и потокобезопасны при раздельных буферах. + +--- + +## 📚 Источники + +* Оригинальный SHA-256: [http://www.zedwood.com/article/cpp-sha256-function](http://www.zedwood.com/article/cpp-sha256-function) +* Оригинальный SHA-512: [http://www.zedwood.com/article/cpp-sha512-function](http://www.zedwood.com/article/cpp-sha512-function) +* HMAC (wiki): [https://en.wikipedia.org/wiki/HMAC](https://en.wikipedia.org/wiki/HMAC) + +--- -* Исходный код [SHA256](http://www.zedwood.com/article/cpp-sha256-function) -* Исходный код [SHA512](http://www.zedwood.com/article/cpp-sha512-function) -* Описание алгоритма [HMAC](https://ru.wikipedia.org/wiki/HMAC) +## 🔗 Связанные проекты -## 🔗 Другие проекты +* [ADVobfuscator](https://github.com/andrivet/ADVobfuscator) +* [obfy](https://github.com/NewYaroslav/obfy) +* [aes-cpp](https://github.com/NewYaroslav/aes-cpp) +* [siphash-cpp](https://github.com/NewYaroslav/siphash-cpp) -- [ADVobfuscator](https://github.com/andrivet/ADVobfuscator) -- [obfy](https://github.com/NewYaroslav/obfy) -- [aescpp](https://github.com/NewYaroslav/aescpp) -- [siphash-hpp](https://github.com/NewYaroslav/siphash-hpp) +--- ## 📝 Лицензия -Проект распространяется под лицензией **MIT**. -Это означает, что вы можете свободно использовать, копировать, модифицировать и распространять код, при условии сохранения оригинального уведомления о лицензии. +MIT — см. [`LICENSE`](./LICENSE). -См. файл [`LICENSE`](./LICENSE) для подробностей.