diff --git a/include/hmac_cpp/hmac.hpp b/include/hmac_cpp/hmac.hpp index f935de3..32b4546 100644 --- a/include/hmac_cpp/hmac.hpp +++ b/include/hmac_cpp/hmac.hpp @@ -52,8 +52,11 @@ namespace hmac_cpp { } /// \brief Streaming HMAC computation context. + /// \thread_safety Not thread-safe. class HMAC_CPP_API HmacContext { public: + /// \brief Construct context for selected hash. + /// \param type Hash function type. explicit HmacContext(TypeHash type) : type_(type), block_size_(0), digest_size_(0) {} /// \brief Initializes the context with a secret key. @@ -114,17 +117,31 @@ namespace hmac_cpp { /// \return HMAC result HMAC_CPP_API std::string get_hmac(const std::vector& key, const std::string &msg, TypeHash type, bool is_hex = true, bool is_upper = false); - /// \brief Computes HMAC from secure_buffer key + /// \brief Compute HMAC using secure_buffer key. + /// \param key Secret key bytes. + /// \param msg Message to authenticate. + /// \param type Hash function type. + /// \param is_hex Return result in hex format. + /// \param is_upper Use uppercase hex. + /// \return HMAC result. inline std::string get_hmac(const secure_buffer& key, const std::string &msg, TypeHash type, bool is_hex = true, bool is_upper = false) { return get_hmac(std::vector(key.begin(), key.end()), msg, type, is_hex, is_upper); } + /// \brief Compute HMAC using string key. + /// \param key_input Secret key as string. + /// \param msg Message to authenticate. + /// \param type Hash function type. + /// \param is_hex Return result in hex format. + /// \param is_upper Use uppercase hex. + /// \return HMAC result. /// \deprecated Prefer overloads that accept std::vector or secure_buffer. HMACCPP_DEPRECATED("use std::vector or secure_buffer overload") inline std::string get_hmac(const std::string& key_input, const std::string &msg, TypeHash type, bool is_hex = true, bool is_upper = false) { return get_hmac(std::vector(key_input.begin(), key_input.end()), msg, type, is_hex, is_upper); } } + namespace hmac = hmac_cpp; #endif // _HMAC_HPP_INCLUDED diff --git a/include/hmac_cpp/hmac_utils.hpp b/include/hmac_cpp/hmac_utils.hpp index c11e6e6..898f18e 100644 --- a/include/hmac_cpp/hmac_utils.hpp +++ b/include/hmac_cpp/hmac_utils.hpp @@ -24,28 +24,49 @@ namespace hmac_cpp { HMAC_CPP_API bool constant_time_equals(const uint8_t* a, size_t a_len, const uint8_t* b, size_t b_len); + /// \brief Compare vectors in constant time. + /// \param a First vector. + /// \param b Second vector. + /// \return true if both vectors are equal. inline bool constant_time_equals(const std::vector& a, const std::vector& b) { return constant_time_equals(a.data(), a.size(), b.data(), b.size()); } + /// \brief Compare strings in constant time. + /// \param a First string. + /// \param b Second string. + /// \return true if both strings are equal. inline bool constant_time_equals(const std::string &a, const std::string &b) { return constant_time_equals(reinterpret_cast(a.data()), a.size(), reinterpret_cast(b.data()), b.size()); } /// \brief Alias for constant_time_equals. - /// Avoids early length checks to mitigate timing attacks. + /// \param a Pointer to first array. + /// \param a_len Length of the first array. + /// \param b Pointer to second array. + /// \param b_len Length of the second array. + /// \return true if both arrays are equal. + /// \note Avoids early length checks to mitigate timing attacks. inline bool constant_time_equal(const uint8_t* a, size_t a_len, const uint8_t* b, size_t b_len) { return constant_time_equals(a, a_len, b, b_len); } + /// \brief Alias for constant_time_equals on vectors. + /// \param a First vector. + /// \param b Second vector. + /// \return true if both vectors are equal. inline bool constant_time_equal(const std::vector& a, const std::vector& b) { return constant_time_equal(a.data(), a.size(), b.data(), b.size()); } + /// \brief Alias for constant_time_equals on strings. + /// \param a First string. + /// \param b Second string. + /// \return true if both strings are equal. inline bool constant_time_equal(const std::string& a, const std::string& b) { return constant_time_equal(reinterpret_cast(a.data()), a.size(), reinterpret_cast(b.data()), b.size()); @@ -61,8 +82,8 @@ namespace hmac_cpp { /// - Salts and iteration counts must be unique per password. /// - Example serialization: {magic|ver|prf|salt|iters|dkLen|…}. - /// \brief - struct Pbkdf2Result { + /// \brief Result of PBKDF2 derivation. + struct Pbkdf2Result { std::vector salt; uint32_t iters; std::vector key; @@ -83,7 +104,14 @@ namespace hmac_cpp { uint32_t iterations, size_t dk_len, Pbkdf2Hash prf = Pbkdf2Hash::Sha256); - /// \brief Derives a key using PBKDF2 from vector-based password and salt + /// \brief Derive key using PBKDF2 from vector-based password and salt. + /// \tparam T Byte type; must be char or uint8_t. + /// \param password Password bytes. + /// \param salt Salt bytes. + /// \param iterations Number of iterations. + /// \param dk_len Desired key length in bytes. + /// \param prf Hash function to use. + /// \return Derived key as byte vector. template inline std::vector pbkdf2( const std::vector& password, @@ -97,7 +125,13 @@ namespace hmac_cpp { iterations, dk_len, prf); } - /// \brief Derives a key using PBKDF2 from string-based password and salt + /// \brief Derive key using PBKDF2 from string-based password and salt. + /// \param password Password bytes as string. + /// \param salt Salt bytes as string. + /// \param iterations Number of iterations. + /// \param dk_len Desired key length in bytes. + /// \param prf Hash function to use. + /// \return Derived key as byte vector. /// \deprecated Use overloads that accept std::vector or secure_buffer. HMACCPP_DEPRECATED("use std::vector or secure_buffer overload") inline std::vector pbkdf2( @@ -110,6 +144,13 @@ namespace hmac_cpp { iterations, dk_len, prf); } + /// \brief Derive key using PBKDF2 from secure_buffer inputs. + /// \param password Password bytes. + /// \param salt Salt bytes. + /// \param iterations Number of iterations. + /// \param dk_len Desired key length in bytes. + /// \param prf Hash function to use. + /// \return Derived key as byte vector. inline std::vector pbkdf2( const secure_buffer& password, const secure_buffer& salt, @@ -120,6 +161,12 @@ namespace hmac_cpp { iterations, dk_len, prf); } + /// \brief Derive key using stored PBKDF2 parameters and vector password. + /// \tparam T Byte type; must be char or uint8_t. + /// \param password Password bytes. + /// \param params Salt, iteration count and key size. + /// \param prf Hash function to use. + /// \return Result structure containing salt, iterations and key. template inline Pbkdf2Result pbkdf2( const std::vector& password, @@ -133,6 +180,11 @@ namespace hmac_cpp { return {params.salt, params.iters, std::move(key)}; } + /// \brief Derive key using stored PBKDF2 parameters and string password. + /// \param password Password bytes as string. + /// \param params Salt, iteration count and key size. + /// \param prf Hash function to use. + /// \return Result structure containing salt, iterations and key. inline Pbkdf2Result pbkdf2( const std::string& password, const Pbkdf2Result& params, @@ -143,6 +195,11 @@ namespace hmac_cpp { return {params.salt, params.iters, std::move(key)}; } + /// \brief Derive key using stored PBKDF2 parameters and secure_buffer password. + /// \param password Password bytes. + /// \param params Salt, iteration count and key size. + /// \param prf Hash function to use. + /// \return Result structure containing salt, iterations and key. inline Pbkdf2Result pbkdf2( const secure_buffer& password, const Pbkdf2Result& params, @@ -168,6 +225,14 @@ namespace hmac_cpp { const void* salt_ptr, size_t salt_len, uint32_t iterations, uint8_t* out_ptr, size_t dk_len) noexcept; + /// \brief Derive PBKDF2 into array using string inputs. + /// \tparam N Output array size. + /// \param prf Hash function to use. + /// \param password Password bytes as string. + /// \param salt Salt bytes as string. + /// \param iterations Number of iterations. + /// \param out Output array for derived key. + /// \return true on success, false on invalid parameters. /// \deprecated Use overloads that accept std::vector or secure_buffer. template HMACCPP_DEPRECATED("use std::vector or secure_buffer overload") @@ -181,6 +246,14 @@ namespace hmac_cpp { iterations, out.data(), out.size()); } + /// \brief Derive PBKDF2 into caller buffer using secure_buffer inputs. + /// \param prf Hash function to use. + /// \param password Password bytes. + /// \param salt Salt bytes. + /// \param iterations Number of iterations. + /// \param out_ptr Output buffer for derived key. + /// \param dk_len Length of output buffer in bytes. + /// \return true on success, false on invalid parameters. inline bool pbkdf2(Pbkdf2Hash prf, const secure_buffer& password, const secure_buffer& salt, @@ -191,6 +264,14 @@ namespace hmac_cpp { iterations, out_ptr, dk_len); } + /// \brief Derive PBKDF2 into array using secure_buffer inputs. + /// \tparam N Output array size. + /// \param prf Hash function to use. + /// \param password Password bytes. + /// \param salt Salt bytes. + /// \param iterations Number of iterations. + /// \param out Output array for derived key. + /// \return true on success, false on invalid parameters. template inline bool pbkdf2(Pbkdf2Hash prf, const secure_buffer& password, @@ -215,6 +296,13 @@ namespace hmac_cpp { const void* salt_ptr, size_t salt_len, uint32_t iterations, uint8_t* out_ptr, size_t dk_len) noexcept; + /// \brief Derive PBKDF2-HMAC-SHA256 into array using string inputs. + /// \tparam N Output array size. + /// \param password Password bytes as string. + /// \param salt Salt bytes as string. + /// \param iterations Number of iterations. + /// \param out Output array for derived key. + /// \return true on success, false on invalid parameters. /// \deprecated Use overloads that accept std::vector or secure_buffer. template HMACCPP_DEPRECATED("use std::vector or secure_buffer overload") @@ -302,7 +390,14 @@ namespace hmac_cpp { iterations, dk_len, prf); } - /// \brief Derives a key using PBKDF2 with pepper from string inputs + /// \brief Derive key using PBKDF2 with pepper from string inputs. + /// \param password Password bytes as string. + /// \param salt Salt bytes as string. + /// \param pepper Pepper bytes as string. + /// \param iterations Number of iterations. + /// \param dk_len Desired length of derived key in bytes. + /// \param prf Hash function to use. + /// \return Derived key as byte vector. /// \deprecated Use overloads that accept std::vector or secure_buffer. HMACCPP_DEPRECATED("use std::vector or secure_buffer overload") inline std::vector pbkdf2_with_pepper( @@ -347,6 +442,10 @@ namespace hmac_cpp { const void* ikm_ptr, size_t ikm_len, const void* salt_ptr, size_t salt_len); + /// \brief HKDF extract step using vector inputs. + /// \param ikm Input keying material. + /// \param salt Salt bytes. + /// \return Pseudorandom key. inline std::vector hkdf_extract_sha256( const std::vector& ikm, const std::vector& salt) { @@ -365,6 +464,11 @@ namespace hmac_cpp { const void* info_ptr, size_t info_len, size_t L); + /// \brief HKDF expand step using vector inputs. + /// \param prk Pseudorandom key. + /// \param info Context information. + /// \param L Output key length in bytes. + /// \return Output keying material. inline std::vector hkdf_expand_sha256( const std::vector& prk, const std::vector& info, @@ -389,6 +493,11 @@ namespace hmac_cpp { const void* salt_ptr, size_t salt_len, const std::string& context); + /// \brief Derive key and IV using vector inputs. + /// \param ikm Input keying material. + /// \param salt Salt bytes. + /// \param context Application-specific context string. + /// \return Derived key and IV. inline KeyIv hkdf_key_iv_256(const std::vector& ikm, const std::vector& salt, const std::string& context) { @@ -402,9 +511,16 @@ namespace hmac_cpp { /// \return Hex-encoded HMAC-SHA256 of the rounded time value /// \throws std::runtime_error if the system time cannot be retrieved HMAC_CPP_API std::string generate_time_token(const std::vector& key, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256); + inline std::string generate_time_token(const secure_buffer& key, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256) { return generate_time_token(std::vector(key.begin(), key.end()), interval_sec, hash_type); } + + /// \brief Generate time token using string key. + /// \param key Secret key as string. + /// \param interval_sec Token rotation interval in seconds. + /// \param hash_type Hash function to use. + /// \return Hex-encoded token. /// \deprecated Prefer overloads that accept std::vector or secure_buffer. HMACCPP_DEPRECATED("use std::vector or secure_buffer overload") inline std::string generate_time_token(const std::string &key, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256) { @@ -422,6 +538,13 @@ namespace hmac_cpp { inline bool is_token_valid(const std::string &token, const secure_buffer& key, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256) { return is_token_valid(token, std::vector(key.begin(), key.end()), interval_sec, hash_type); } + + /// \brief Validate time token using string key. + /// \param token Token to validate. + /// \param key Secret key as string. + /// \param interval_sec Token rotation interval in seconds. + /// \param hash_type Hash function to use. + /// \return true if token is valid. /// \deprecated Prefer overloads that accept std::vector or secure_buffer. HMACCPP_DEPRECATED("use std::vector or secure_buffer overload") inline bool is_token_valid(const std::string &token, const std::string &key, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256) { @@ -436,9 +559,17 @@ namespace hmac_cpp { /// \return Hex-encoded HMAC-SHA256 of the concatenated timestamp and fingerprint /// \throws std::runtime_error if the system time cannot be retrieved HMAC_CPP_API std::string generate_time_token(const std::vector& key, const std::string &fingerprint, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256); + inline std::string generate_time_token(const secure_buffer& key, const std::string &fingerprint, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256) { return generate_time_token(std::vector(key.begin(), key.end()), fingerprint, interval_sec, hash_type); } + + /// \brief Generate fingerprint-bound token using string key. + /// \param key Secret key as string. + /// \param fingerprint Client identifier. + /// \param interval_sec Token rotation interval in seconds. + /// \param hash_type Hash function to use. + /// \return Hex-encoded token. /// \deprecated Prefer overloads that accept std::vector or secure_buffer. HMACCPP_DEPRECATED("use std::vector or secure_buffer overload") inline std::string generate_time_token(const std::string &key, const std::string &fingerprint, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256) { @@ -454,9 +585,18 @@ namespace hmac_cpp { /// \return true if the token is valid within the ±1 interval range; false otherwise /// \throws std::runtime_error if the system time cannot be retrieved HMAC_CPP_API bool is_token_valid(const std::string &token, const std::vector& key, const std::string &fingerprint, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256); + inline bool is_token_valid(const std::string &token, const secure_buffer& key, const std::string &fingerprint, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256) { return is_token_valid(token, std::vector(key.begin(), key.end()), fingerprint, interval_sec, hash_type); } + + /// \brief Validate fingerprint-bound token using string key. + /// \param token Token to validate. + /// \param key Secret key as string. + /// \param fingerprint Client identifier. + /// \param interval_sec Token rotation interval in seconds. + /// \param hash_type Hash function to use. + /// \return true if token is valid. /// \deprecated Prefer overloads that accept std::vector or secure_buffer. HMACCPP_DEPRECATED("use std::vector or secure_buffer overload") inline bool is_token_valid(const std::string &token, const std::string &key, const std::string &fingerprint, int interval_sec = 60, TypeHash hash_type = TypeHash::SHA256) { @@ -486,6 +626,12 @@ namespace hmac_cpp { return get_hotp_code(key.data(), key.size(), counter, digits, hash_type); } + /// \brief Compute HOTP code using secure buffer key. + /// \param key Secret key bytes. + /// \param counter Moving counter. + /// \param digits Number of digits in OTP. + /// \param hash_type Hash function to use. + /// \return One-Time Password. inline int get_hotp_code(const secure_buffer& key, uint64_t counter, int digits = 6, TypeHash hash_type = TypeHash::SHA1) { return get_hotp_code(key.data(), key.size(), counter, digits, hash_type); } @@ -550,6 +696,13 @@ namespace hmac_cpp { return get_totp_code_at(key.data(), key.size(), timestamp, period, digits, hash_type); } + /// \brief Compute TOTP code for timestamp using secure buffer key. + /// \param key Secret key bytes. + /// \param timestamp UNIX timestamp in seconds. + /// \param period Time step in seconds. + /// \param digits Number of digits in OTP. + /// \param hash_type Hash function to use. + /// \return TOTP code. inline int get_totp_code_at( const secure_buffer& key, uint64_t timestamp, @@ -608,6 +761,12 @@ namespace hmac_cpp { return get_totp_code(key.data(), key.size(), period, digits, hash_type); } + /// \brief Compute current TOTP code using secure buffer key. + /// \param key Secret key bytes. + /// \param period Time step in seconds. + /// \param digits Number of digits in OTP. + /// \param hash_type Hash function to use. + /// \return TOTP code. inline int get_totp_code(const secure_buffer& key, int period = 30, int digits = 6, TypeHash hash_type = TypeHash::SHA1) { return get_totp_code(key.data(), key.size(), period, digits, hash_type); } diff --git a/include/hmac_cpp/secure_buffer.hpp b/include/hmac_cpp/secure_buffer.hpp index 09ee259..1b260da 100644 --- a/include/hmac_cpp/secure_buffer.hpp +++ b/include/hmac_cpp/secure_buffer.hpp @@ -36,9 +36,17 @@ struct secure_buffer { static_assert(std::is_trivial::value, "secure_buffer requires trivial type"); secure_buffer() = default; + + /// \brief Construct with n default-initialized elements. + /// \param n Element count. explicit secure_buffer(size_t n) : buf(n) {} + + /// \brief Construct from vector, moving its contents. + /// \param v Source vector. explicit secure_buffer(std::vector&& v) : buf(std::move(v)) {} - /// \brief Constructs from std::string rvalue and zeroizes the source. + + /// \brief Construct from std::string rvalue and zeroize the source. + /// \param s Source string. template::value, int>::type = 0> explicit secure_buffer(std::string&& s) : buf(s.begin(), s.end()) { if (!s.empty()) { @@ -46,6 +54,7 @@ struct secure_buffer { s.clear(); } } + /// \brief Zeroize contents on destruction. ~secure_buffer() { secure_zero(buf.data(), buf.size() * sizeof(T)); } T* data() { return buf.data(); }