From 39c10bd112afcfc2990b7cf52b74d7e4a9943169 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Sat, 6 Sep 2025 00:16:58 +0300 Subject: [PATCH] docs(example): clarify streaming comment Add note about chunked processing for HMAC example. --- CMakeLists.txt | 18 +++++++--- README.md | 8 +++++ example_pbkdf2.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++ example_streaming.cpp | 42 +++++++++++++++++++++++ 4 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 example_pbkdf2.cpp create mode 100644 example_streaming.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 89c5303..097dee5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,11 +33,19 @@ if(NOT TARGET hmac_cpp::hmac_cpp) add_library(hmac_cpp::hmac_cpp ALIAS hmac_cpp) endif() -if(HMACCPP_BUILD_EXAMPLES) - add_executable(example example.cpp) - target_link_libraries(example PRIVATE hmac_cpp) - target_include_directories(example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) -endif() + if(HMACCPP_BUILD_EXAMPLES) + add_executable(example example.cpp) + target_link_libraries(example PRIVATE hmac_cpp) + target_include_directories(example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) + + add_executable(example_streaming example_streaming.cpp) + target_link_libraries(example_streaming PRIVATE hmac_cpp) + target_include_directories(example_streaming PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) + + add_executable(example_pbkdf2 example_pbkdf2.cpp) + target_link_libraries(example_pbkdf2 PRIVATE hmac_cpp) + target_include_directories(example_pbkdf2 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) + endif() include(CMakePackageConfigHelpers) install(TARGETS hmac_cpp EXPORT hmac_cppTargets DESTINATION lib) diff --git a/README.md b/README.md index 9040ab1..8a46643 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,14 @@ int main() { **Note:** avoid checking input lengths before calling `constant_time_equal`. Early length comparisons can leak information through timing side channels. +## PBKDF2 Recommended Parameters + +| Target | Iterations | Derived key length | PRF | +|---------|-----------:|------------------:|-----| +| Desktop | 600000 | 32 bytes | HMAC-SHA256 | +| Laptop | 300000 | 32 bytes | HMAC-SHA256 | +| Mobile | 150000 | 32 bytes | HMAC-SHA256 | + ## 📚 Resources * Original [SHA256 implementation](http://www.zedwood.com/article/cpp-sha256-function) diff --git a/example_pbkdf2.cpp b/example_pbkdf2.cpp new file mode 100644 index 0000000..822139e --- /dev/null +++ b/example_pbkdf2.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +#include "hmac_cpp/hmac.hpp" +#include "hmac_cpp/hmac_utils.hpp" + +static std::vector from_hex(const std::string& hex) { + std::vector out; + out.reserve(hex.size() / 2); + for (size_t i = 0; i < hex.size(); i += 2) { + unsigned int byte; + std::stringstream ss; + ss << std::hex << hex.substr(i, 2); + ss >> byte; + out.push_back(static_cast(byte)); + } + return out; +} + +static std::string serialize(const std::vector& salt, + uint32_t iters, + size_t dk_len, + hmac::Pbkdf2Hash prf) { + std::string salt_hex = hmac::to_hex( + std::string(reinterpret_cast(salt.data()), salt.size())); + std::ostringstream oss; + oss << salt_hex << '|' << iters << '|' << dk_len << '|' << static_cast(prf); + return oss.str(); +} + +static bool deserialize(const std::string& s, + std::vector& salt, + uint32_t& iters, + size_t& dk_len, + hmac::Pbkdf2Hash& prf) { + std::istringstream iss(s); + std::string salt_hex, iters_str, dk_len_str, prf_str; + if (!std::getline(iss, salt_hex, '|')) return false; + if (!std::getline(iss, iters_str, '|')) return false; + if (!std::getline(iss, dk_len_str, '|')) return false; + if (!std::getline(iss, prf_str, '|')) return false; + salt = from_hex(salt_hex); + iters = static_cast(std::stoul(iters_str)); + dk_len = static_cast(std::stoul(dk_len_str)); + prf = static_cast(std::stoi(prf_str)); + return true; +} + +int main() { + std::vector password{'s','e','c','r','e','t'}; + std::vector salt{0,1,2,3,4,5,6,7}; + uint32_t iters = 100000; + size_t dk_len = 32; + hmac::Pbkdf2Hash prf = hmac::Pbkdf2Hash::Sha256; + + auto dk = hmac::pbkdf2(password, salt, iters, dk_len, prf); + + std::string header = serialize(salt, iters, dk_len, prf); + std::cout << "serialized params: " << header << '\n'; + + std::vector salt2; uint32_t iters2; size_t dk_len2; hmac::Pbkdf2Hash prf2; + if (!deserialize(header, salt2, iters2, dk_len2, prf2)) { + std::cerr << "failed to parse header" << std::endl; + return 1; + } + auto dk2 = hmac::pbkdf2(password, salt2, iters2, dk_len2, prf2); + + bool same = hmac::constant_time_equal( + std::string(reinterpret_cast(dk.data()), dk.size()), + std::string(reinterpret_cast(dk2.data()), dk2.size())); + std::cout << "keys match? " << (same ? "yes" : "no") << std::endl; + + // dk would be used to decrypt your configuration here + return same ? 0 : 1; +} diff --git a/example_streaming.cpp b/example_streaming.cpp new file mode 100644 index 0000000..dd8b970 --- /dev/null +++ b/example_streaming.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +// stream large files in chunks and verify +#include "hmac_cpp/hmac.hpp" +#include "hmac_cpp/hmac_utils.hpp" + +int main() { + const std::string key = "supersecret"; + const std::string path = "large.bin"; // path to large file + + hmac::HmacContext ctx(hmac::TypeHash::SHA256); + ctx.init(key.data(), key.size()); + + std::ifstream in(path, std::ios::binary); + if (!in) { + std::cerr << "cannot open " << path << "\n"; + return 1; + } + + std::array buf{}; + while (in.good()) { + in.read(buf.data(), buf.size()); + std::streamsize got = in.gcount(); + if (got > 0) { + ctx.update(buf.data(), static_cast(got)); + } + } + + std::array mac{}; + ctx.final(mac.data(), mac.size()); + + std::string mac_hex = hmac::to_hex( + std::string(reinterpret_cast(mac.data()), mac.size())); + + const std::string expected_hex = ""; // known good value + bool ok = hmac::constant_time_equal(mac_hex, expected_hex); + std::cout << "HMAC valid? " << (ok ? "yes" : "no") << std::endl; + return ok ? 0 : 1; +}