Compare commits
3 Commits
3d89164109
...
a4aed41ff1
Author | SHA1 | Date | |
---|---|---|---|
a4aed41ff1 | |||
9cc9b48c18 | |||
d3efc643f5 |
10
include/hash/hash.hpp
Normal file
10
include/hash/hash.hpp
Normal 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
|
19
include/jwt/jwt.h
Normal file
19
include/jwt/jwt.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef JWT_H
|
||||
#define JWT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
char* create_token(const char* user_id, const char* secret);
|
||||
|
||||
char* get_payload(const char* token);
|
||||
|
||||
int verify_token(const char* token, const char* secret);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // JWT_H
|
6
include/server/auth.h
Normal file
6
include/server/auth.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef SERVER_AUTH_H
|
||||
#define SERVER_AUTH_H
|
||||
|
||||
// Your code here
|
||||
|
||||
#endif // SERVER_AUTH_H
|
@ -1,5 +1,5 @@
|
||||
#ifndef KQM_TYPES_H
|
||||
#define KQM_TYPES_H
|
||||
#ifndef SERVER_TYPES_H
|
||||
#define SERVER_TYPES_H
|
||||
|
||||
#include <civetweb.h>
|
||||
|
||||
@ -8,4 +8,4 @@ typedef struct mg_context mg_context;
|
||||
typedef struct mg_connection mg_connection;
|
||||
typedef struct mg_request_info mg_request_info;
|
||||
|
||||
#endif // KQM_TYPES_H
|
||||
#endif // SERVER_TYPES_H
|
79
src/db/auth.cpp
Normal file
79
src/db/auth.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "auth.h"
|
||||
|
||||
#include "hash/hash.hpp"
|
||||
|
||||
#include <print>
|
||||
|
||||
#include <leveldb/db.h>
|
||||
|
||||
using leveldb_ptr = leveldb::DB*;
|
||||
|
||||
auto user_db = leveldb_ptr{nullptr};
|
||||
auto iter_round = 10000;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
auto open_user_db() -> int
|
||||
{
|
||||
auto opts = leveldb::Options{};
|
||||
|
||||
opts.create_if_missing = true;
|
||||
|
||||
auto status = leveldb::DB::Open(opts, "db/user", &user_db);
|
||||
|
||||
if (!status.ok()) {
|
||||
std::println(stderr, "Failed to open user database: {}", status.ToString());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto close_user_db() -> void
|
||||
{
|
||||
delete user_db;
|
||||
}
|
||||
|
||||
auto check_user_exists(const char* user_id, int* result) -> int
|
||||
{
|
||||
auto value = std::string{};
|
||||
auto status = user_db->Get(leveldb::ReadOptions{}, user_id, &value);
|
||||
if (status.ok()) {
|
||||
*result = 1;
|
||||
} else if (status.IsNotFound()) {
|
||||
*result = 0;
|
||||
} else {
|
||||
std::println(stderr, "Failed to check user existence: {}", status.ToString());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto set_user_password(const char* user_id, const char* password) -> int
|
||||
{
|
||||
auto status = user_db->Put(leveldb::WriteOptions{}, user_id, generate_hash(password, iter_round));
|
||||
if (!status.ok()) {
|
||||
std::println(stderr, "Failed to set user password: {}", status.ToString());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto login(const char* user_id, const char* password, int* result) -> int
|
||||
{
|
||||
auto value = std::string{};
|
||||
auto status = user_db->Get(leveldb::ReadOptions{}, user_id, &value);
|
||||
if (!status.ok()) {
|
||||
std::println(stderr, "Failed to login: {}", status.ToString());
|
||||
return 0;
|
||||
}
|
||||
*result = validate_password(password, value.data());
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto logout(const char* user_id) -> int
|
||||
{
|
||||
leveldb::WriteOptions write_options;
|
||||
leveldb::Status status = user_db->Delete(write_options, user_id);
|
||||
return status.ok();
|
||||
}
|
||||
}
|
91
src/hash/hash.cpp
Normal file
91
src/hash/hash.cpp
Normal 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;
|
||||
}
|
||||
|
46
src/jwt/jwt.cpp
Normal file
46
src/jwt/jwt.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include "jwt.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <jwt-cpp/jwt.h>
|
||||
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
auto create_token(const char* user_id, const char* secret) -> char*
|
||||
{
|
||||
auto token = jwt::create()
|
||||
.set_type("JWS")
|
||||
.set_issuer("KeqingMoe")
|
||||
.set_subject("user_id")
|
||||
.set_audience("web_client")
|
||||
.set_expires_at(std::chrono::system_clock::now() + 3s)
|
||||
.set_payload_claim("user_id", jwt::claim(std::string{user_id}))
|
||||
.sign(jwt::algorithm::hs256{secret});
|
||||
return strdup(token.c_str());
|
||||
}
|
||||
|
||||
auto get_payload(const char* token) -> char*
|
||||
{
|
||||
auto decoded_token = jwt::decode(token);
|
||||
auto payload = decoded_token.get_payload_claim("user_id").as_string();
|
||||
return strdup(payload.c_str());
|
||||
}
|
||||
|
||||
auto verify_token(const char* token, const char* secret) -> int
|
||||
{
|
||||
try {
|
||||
auto decoded_token = jwt::decode(token);
|
||||
|
||||
auto verifier = jwt::verify()
|
||||
.allow_algorithm(jwt::algorithm::hs256{secret})
|
||||
.with_issuer("KeqingMoe")
|
||||
.with_subject("user_id");
|
||||
verifier.verify(decoded_token);
|
||||
return 1;
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user