添加 hash 加盐和验证功能

This commit is contained in:
keqingmoe 2024-12-22 19:44:41 +08:00
parent d3efc643f5
commit 9cc9b48c18
4 changed files with 107 additions and 6 deletions

10
include/hash/hash.hpp Normal file
View File

@ -0,0 +1,10 @@
#ifndef HASH_HPP
#define HASH_HPP
#include <string>
#include <string_view>
auto generate_hash(const std::string_view password, std::size_t rounds) -> std::string;
auto validate_password(const std::string_view password, const std::string_view hash) -> bool;
#endif // HASH_HPP

View File

@ -6,11 +6,11 @@ extern "C"
{ {
#endif #endif
char* create_token(const char* user, const char* secret); auto create_token(const char* user_id, const char* secret) noexcept -> char*;
char* get_payload(const char* token); auto get_payload(const char* token) noexcept -> char*;
int verify_token(const char* token, const char* secret); auto verify_token(const char* token, const char* secret) noexcept -> int;
#ifdef __cplusplus #ifdef __cplusplus
} }

91
src/hash/hash.cpp Normal file
View File

@ -0,0 +1,91 @@
#include <hash.hpp>
#include <cryptopp/algparam.h>
#include <cryptopp/cryptlib.h>
#include <cryptopp/filters.h>
#include <cryptopp/hex.h>
#include <cryptopp/modes.h>
#include <cryptopp/osrng.h>
#include <cryptopp/pwdbased.h>
#include <cryptopp/sha.h>
#include <array>
#include <format>
#include <vector>
using namespace CryptoPP;
auto to_byte_ptr(auto* p) noexcept -> const byte*
{
return reinterpret_cast<const byte*>(p);
}
auto generate_salt(size_t length = 16) -> std::string
{
auto prng = AutoSeededRandomPool{};
auto salt = std::vector<byte>(length);
prng.GenerateBlock(salt.data(), length);
auto salt_str = std::string{};
auto encoder = HexEncoder{new StringSink(salt_str)};
encoder.Put(salt.data(), length);
encoder.MessageEnd();
return salt_str;
}
auto hash_password_with_pbkdf2(const std::string_view password, const std::string_view salt, std::size_t iterations)
-> std::string
{
auto decoded_salt = std::string{};
auto decoder = HexDecoder{};
decoder.Attach(new StringSink(decoded_salt));
decoder.Put(to_byte_ptr(salt.data()), salt.size());
decoder.MessageEnd();
auto pbkdf2 = PKCS5_PBKDF2_HMAC<SHA256>{};
auto derived_key = std::array<byte, SHA256::DIGESTSIZE>{};
pbkdf2.DeriveKey(derived_key.data(),
derived_key.size(),
0,
to_byte_ptr(password.data()),
password.size(),
to_byte_ptr(decoded_salt.data()),
decoded_salt.size(),
iterations,
0);
auto hashed_password = std::string{};
auto encoder = HexEncoder{new StringSink(hashed_password)};
encoder.Put(derived_key.data(), derived_key.size());
encoder.MessageEnd();
return hashed_password;
}
auto generate_hash(const std::string_view password, std::size_t rounds) -> std::string
{
auto salt = generate_salt();
auto hashed_password = hash_password_with_pbkdf2(password, salt, rounds);
return std::format("{}${}${}", salt, rounds, hashed_password);
}
auto validate_password(const std::string_view password, const std::string_view hash) -> bool
{
auto first_delimiter = hash.find('$');
auto second_delimiter = hash.find('$', first_delimiter + 1);
if (first_delimiter == std::string::npos || second_delimiter == std::string::npos) {
return false;
}
auto stored_salt = hash.substr(0, first_delimiter);
auto stored_rounds = std::size_t{};
{
auto first = hash.data() + first_delimiter + 1;
auto last = hash.data() + second_delimiter;
auto [ptr, ec] = std::from_chars(first, last, stored_rounds);
if (ec != std::errc{}) return false;
}
auto stored_hashed_password = hash.substr(second_delimiter + 1);
auto entered_hashed_password = hash_password_with_pbkdf2(password, stored_salt, stored_rounds);
return entered_hashed_password == stored_hashed_password;
}

View File

@ -8,7 +8,7 @@ using namespace std::literals::chrono_literals;
extern "C" extern "C"
{ {
char* create_token(const char* user_id, const char* secret) auto create_token(const char* user_id, const char* secret) noexcept -> char*
{ {
auto token = jwt::create() auto token = jwt::create()
.set_type("JWS") .set_type("JWS")
@ -21,14 +21,14 @@ extern "C"
return strdup(token.c_str()); return strdup(token.c_str());
} }
char* get_payload(const char* token) auto get_payload(const char* token) noexcept -> char*
{ {
auto decoded_token = jwt::decode(token); auto decoded_token = jwt::decode(token);
auto payload = decoded_token.get_payload_claim("user_id").as_string(); auto payload = decoded_token.get_payload_claim("user_id").as_string();
return strdup(payload.c_str()); return strdup(payload.c_str());
} }
int verify_token(const char* token, const char* secret) auto verify_token(const char* token, const char* secret) noexcept -> int
{ {
try { try {
auto decoded_token = jwt::decode(token); auto decoded_token = jwt::decode(token);