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
|
#ifndef SERVER_TYPES_H
|
||||||
#define KQM_TYPES_H
|
#define SERVER_TYPES_H
|
||||||
|
|
||||||
#include <civetweb.h>
|
#include <civetweb.h>
|
||||||
|
|
||||||
@ -8,4 +8,4 @@ typedef struct mg_context mg_context;
|
|||||||
typedef struct mg_connection mg_connection;
|
typedef struct mg_connection mg_connection;
|
||||||
typedef struct mg_request_info mg_request_info;
|
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