finished ClientCredential_create

This commit is contained in:
Timerix 2025-09-28 17:18:17 +05:00
parent 37f38feec7
commit 4fda6ae9e8
15 changed files with 322 additions and 241 deletions

View File

@ -1,5 +0,0 @@
#pragma once
#include "tlibc/errors.h"
Result(void) client_run();
Result(void) server_run(cstr server_endpoint_str);

View File

@ -1,209 +0,0 @@
#include "chat.h"
#include <readline/readline.h>
#include <readline/history.h>
#include "term.h"
#include "network/socket.h"
#include "cryptography/cryptography.h"
#include "tlibc/string/StringBuilder.h"
static const str greeting_art = STR(
" ^,,^ |\n"
" ( •·•) Meum! (o.o`7\n"
" / ` | Meum... |`˜ \\\n"
"\\(_,J J L l`,)/\n"
);
static const str farewell_art = STR(
" ^,,^ |\n"
" ( -.-) (>,<`7\n"
" / ` | Goodbye! |`˜ \\\n"
"\\(_,J J L l`,)/\n"
);
static Result(void) commandExec(str command, bool* stop);
typedef struct ClientCredential {
str username;
u8 passhash_lvl2[password_hash_size];
EncryptorAES aes_enc;
DecryptorAES aes_dec;
EncryptorRSA rsa_enc;
DecryptorRSA rsa_dec;
} ClientCredential;
typedef struct ServerConnection {
EndpointIPv4 server_end;
Socket system_socket;
Socket content_socket;
EncryptorRSA rsa_enc;
EncryptorAES session_aes_enc;
DecryptorAES session_aes_dec;
} ServerConnection;
Result(ServerConnection*) connectToServer(EndpointIPv4 server_end, str server_key);
Result(void) client_run() {
Deferral(32);
if(!term_init()){
Return RESULT_ERROR("can't init terminal", false);
}
using_history();
fputs(greeting_art.data, stdout);
char* command_input_prev = NULL;
char* command_input_raw = NULL;
Defer(rl_free(command_input_prev));
str command_input = str_null;
bool stop = false;
while((command_input_raw = readline("> ")) && !stop){
rl_free(command_input_prev);
command_input_prev = command_input_raw;
command_input = str_from_cstr(command_input_raw);
str_trim(&command_input, true);
if(command_input.size == 0)
continue;
add_history(command_input.data);
Result(void) com_result = commandExec(command_input, &stop);
if(com_result.error){
str e_str = Error_toStr(com_result.error);
printfe("%s\n", e_str.data);
free(e_str.data);
Error_free(com_result.error);
}
}
Return RESULT_VOID;
}
#define is_alias(LITERAL) str_equals(command, STR(LITERAL))
static ClientCredential* client_credential = NULL;
static Result(void) commandExec(str command, bool* stop){
Deferral(64);
char answer_buf[512];
const u32 answer_buf_size = sizeof(answer_buf);
if(is_alias("q") || is_alias("quit") || is_alias("exit")){
fputs(farewell_art.data, stdout);
*stop = true;
}
else if(is_alias("clear")){
term_clear();
}
else if(is_alias("h") || is_alias("help")){
puts(
"COMMANDS:\n"
"q, quit, exit Close the program.\n"
"clear Clear the screen.\n"
"j, join Join a server.\n"
"c, connect Connect to a server you joined.\n"
);
}
else if (is_alias("j") || is_alias("join")){
puts("Enter server address (ip:port): ");
fgets(answer_buf, answer_buf_size, stdin);
EndpointIPv4 new_server_end;
try_void(EndpointIPv4_parse(answer_buf, &new_server_end));
puts("Enter server key (): ");
fgets(answer_buf, answer_buf_size, stdin);
str new_server_key = str_from_cstr(answer_buf);
try(ServerConnection* conn, p, connectToServer(new_server_end, new_server_key));
}
else if(is_alias("c") || is_alias("connect")){
// TODO: read saved servers from database
}
else {
Return RESULT_ERROR_FMT("unknown kommand: '%s'\n"
"Use 'h' to see list of avaliable commands\n",
command.data);
}
Return RESULT_VOID;
}
void ClientCredential_free(ClientCredential* cred){
if(cred == NULL)
return;
free(cred->username.data);
free(cred);
}
#define __passhash_lvl_iter 1e5
Result(ClientCredential*) ClientCredential_create(str username, str password){
Deferral(32);
ClientCredential* cred = (ClientCredential*)malloc(sizeof(ClientCredential));
memset(cred, 0, sizeof(ClientCredential));
bool success = false;
Defer(
if(!success)
ClientCredential_free(cred);
);
cred->username = str_copy(username);
// concat password and username
StringBuilder sb = StringBuilder_alloc(username.size + password.size + 1);
Defer(StringBuilder_destroy(&sb));
StringBuilder_append_str(&sb, password);
StringBuilder_append_str(&sb, username);
// lvl 1 hash - is being used to generate rsa keys
u8 passhash_lvl1[password_hash_size];
hash_password(StringBuilder_getStr(&sb), passhash_lvl1, __passhash_lvl_iter);
// lvl 2 hash - is being used to authorize client on server
str _passhash_lvl1_str = str_construct((void*)passhash_lvl1, password_hash_size, false);
hash_password(_passhash_lvl1_str, cred->passhash_lvl2, __passhash_lvl_iter);
success = true;
return RESULT_VALUE(p, cred);
}
void ServerConnection_close(ServerConnection* conn){
if(conn == NULL)
return;
socket_close(conn->system_socket);
socket_close(conn->content_socket);
free(conn);
}
Result(ServerConnection*) connectToServer(EndpointIPv4 server_end, str server_key){
Deferral(64);
if(client_credential == NULL){
}
str end_str = EndpointIPv4_toStr(server_end);
Defer(free(end_str.data));
if(EndpointIPv4_is_invalid(server_end)){
Return RESULT_ERROR_FMT("endpoint is invalid: %s", end_str.data);
}
ServerConnection* conn = (ServerConnection*)malloc(sizeof(ServerConnection));
memset(conn, 0, sizeof(ServerConnection));
bool success = false;
Defer(
if(!success)
ServerConnection_close(conn);
);
printf("connecting to server %s\n", end_str.data);
try(conn->system_socket, i, socket_open_TCP());
try_void(socket_connect(conn->system_socket, server_end));
// ask user name and password
// calculate key pair from password hash
// send client public key to server
// request server info
// show server info
// save server info to user's db
// hash password more times
// request log in
// if not registered, request registration and then log in
success = true;
Return RESULT_VALUE(p, conn);
}

View File

@ -0,0 +1,74 @@
#include "client.h"
#include "tlibc/string/StringBuilder.h"
void ClientCredential_free(ClientCredential* cred){
if(cred == NULL)
return;
free(cred->username.data);
free(cred->aes_key.data);
free(cred->token.data);
RSA_destroyPrivateKey(&cred->sk);
RSA_destroyPublicKey(&cred->pk);
free(cred);
}
#define __passhash_lvl_iter 1e5
Result(ClientCredential*) ClientCredential_create(str username, str password){
Deferral(32);
ClientCredential* cred = (ClientCredential*)malloc(sizeof(ClientCredential));
memset(cred, 0, sizeof(ClientCredential));
bool success = false;
Defer(
if(!success)
free(cred);
);
cred->username = str_copy(username);
Defer(
if(!success)
free(cred->username.data);
);
// concat password and username
StringBuilder sb = StringBuilder_alloc(username.size + password.size + 1);
Defer(StringBuilder_destroy(&sb));
StringBuilder_append_str(&sb, password);
StringBuilder_append_str(&sb, username);
Array(u8) password_and_username = str_castTo_Array(StringBuilder_getStr(&sb));
// lvl 1 hash - is used to generate RSA keys
Array(u8) passhash_lvl1 = Array_alloc(u8, password_hash_size);
cred->aes_key = Array_alloc(u8, password_hash_size);
cred->token = Array_alloc(u8, password_hash_size);
Defer(
free(passhash_lvl1.data);
if(!success){
free(cred->aes_key.data);
free(cred->token.data);
}
);
hash_password(password_and_username, passhash_lvl1.data, __passhash_lvl_iter);
// lvl 2 hash - is used as AES key
hash_password(passhash_lvl1, cred->aes_key.data, __passhash_lvl_iter);
// lvl 3 hash - is used to authorize client on server
hash_password(cred->aes_key, cred->token.data, __passhash_lvl_iter);
// generate client rsa keys from password hash
br_hmac_drbg_context passhash_based_rng = { .vtable = &br_hmac_drbg_vtable };
br_hmac_drbg_init(&passhash_based_rng, &br_sha256_vtable, passhash_lvl1.data, password_hash_size);
try_void(RSA_generateKeyPair(4096, &cred->sk, &cred->pk, &passhash_based_rng.vtable));
Defer(
if(!success){
RSA_destroyPrivateKey(&cred->sk);
RSA_destroyPublicKey(&cred->pk);
}
);
DecryptorRSA_construct(&cred->rsa_dec, &cred->sk);
EncryptorRSA_construct(&cred->rsa_enc, &cred->pk);
DecryptorAES_construct(&cred->aes_dec, cred->aes_key);
EncryptorAES_construct(&cred->aes_enc, cred->aes_key);
success = true;
Return RESULT_VALUE(p, cred);
}

View File

@ -0,0 +1,44 @@
#include "client.h"
void ServerConnection_close(ServerConnection* conn){
if(conn == NULL)
return;
socket_close(conn->system_socket);
socket_close(conn->content_socket);
free(conn);
}
Result(ServerConnection*) ServerConnection_open(ClientCredential* client_credential, EndpointIPv4 server_end, str server_key){
Deferral(64);
str end_str = EndpointIPv4_toStr(server_end);
Defer(free(end_str.data));
if(EndpointIPv4_is_invalid(server_end)){
Return RESULT_ERROR_FMT("endpoint is invalid: %s", end_str.data);
}
ServerConnection* conn = (ServerConnection*)malloc(sizeof(ServerConnection));
memset(conn, 0, sizeof(ServerConnection));
bool success = false;
Defer(
if(!success)
ServerConnection_close(conn);
);
printf("connecting to server %s\n", end_str.data);
try(conn->system_socket, i, socket_open_TCP());
try_void(socket_connect(conn->system_socket, server_end));
// ask user name and password
// calculate key pair from password hash
// send client public key to server
// request server info
// show server info
// save server info to user's db
// hash password more times
// request log in
// if not registered, request registration and then log in
success = true;
Return RESULT_VALUE(p, conn);
}

118
src/client/client.c Normal file
View File

@ -0,0 +1,118 @@
// readline.h doesn't include stdio.h
// This bug is older than me)))
#include "client.h"
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "term.h"
static const str greeting_art = STR(
" ^,,^ |\n"
" ( •·•) Meum! (o.o`7\n"
" / ` | Meum... |`˜ \\\n"
"\\(_,J J L l`,)/\n"
);
static const str farewell_art = STR(
" ^,,^ |\n"
" ( -.-) (>,<`7\n"
" / ` | Goodbye! |`˜ \\\n"
"\\(_,J J L l`,)/\n"
);
static ClientCredential* client_credential = NULL;
static Result(void) commandExec(str command, bool* stop);
static Result(void) askUserNameAndPassword(ClientCredential** cred){
Deferral(8);
char username[1024];
char password[1024];
scanf("username: %s", username);
scanf("password: %s", password);
try(*cred, p, ClientCredential_create(str_from_cstr(username), str_from_cstr(password)));
Return RESULT_VOID;
}
Result(void) client_run() {
Deferral(32);
if(!term_init()){
Return RESULT_ERROR("can't init terminal", false);
}
using_history();
fputs(greeting_art.data, stdout);
try_void(askUserNameAndPassword(&client_credential));
char* command_input_prev = NULL;
char* command_input_raw = NULL;
Defer(rl_free(command_input_prev));
str command_input = str_null;
bool stop = false;
while((command_input_raw = readline("> ")) && !stop){
rl_free(command_input_prev);
command_input_prev = command_input_raw;
command_input = str_from_cstr(command_input_raw);
str_trim(&command_input, true);
if(command_input.size == 0)
continue;
add_history(command_input.data);
Result(void) com_result = commandExec(command_input, &stop);
if(com_result.error){
str e_str = Error_toStr(com_result.error);
printfe("%s\n", e_str.data);
free(e_str.data);
Error_free(com_result.error);
}
}
ClientCredential_free(client_credential);
Return RESULT_VOID;
}
#define is_alias(LITERAL) str_equals(command, STR(LITERAL))
static Result(void) commandExec(str command, bool* stop){
Deferral(64);
char answer_buf[512];
const u32 answer_buf_size = sizeof(answer_buf);
if(is_alias("q") || is_alias("quit") || is_alias("exit")){
fputs(farewell_art.data, stdout);
*stop = true;
}
else if(is_alias("clear")){
term_clear();
}
else if(is_alias("h") || is_alias("help")){
puts(
"COMMANDS:\n"
"q, quit, exit Close the program.\n"
"clear Clear the screen.\n"
"j, join Join a server.\n"
"c, connect Connect to a server you joined.\n"
);
}
else if (is_alias("j") || is_alias("join")){
puts("Enter server address (ip:port): ");
fgets(answer_buf, answer_buf_size, stdin);
EndpointIPv4 new_server_end;
try_void(EndpointIPv4_parse(answer_buf, &new_server_end));
puts("Enter server key (): ");
fgets(answer_buf, answer_buf_size, stdin);
str new_server_key = str_from_cstr(answer_buf);
try(ServerConnection* conn, p, ServerConnection_open(client_credential, new_server_end, new_server_key));
// TODO: close server connection somewhere
}
else if(is_alias("c") || is_alias("connect")){
// TODO: read saved servers from database
}
else {
Return RESULT_ERROR_FMT("unknown kommand: '%s'\n"
"Use 'h' to see list of avaliable commands\n",
command.data);
}
Return RESULT_VOID;
}

32
src/client/client.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include "network/socket.h"
#include "cryptography/cryptography.h"
Result(void) client_run();
typedef struct ClientCredential {
str username;
Array(u8) aes_key;
Array(u8) token;
br_rsa_private_key sk;
br_rsa_public_key pk;
EncryptorRSA rsa_enc;
DecryptorRSA rsa_dec;
EncryptorAES aes_enc;
DecryptorAES aes_dec;
} ClientCredential;
Result(ClientCredential*) ClientCredential_create(str username, str password);
void ClientCredential_free(ClientCredential* cred);
typedef struct ServerConnection {
EndpointIPv4 server_end;
Socket system_socket;
Socket content_socket;
EncryptorRSA rsa_enc;
EncryptorAES session_aes_enc;
DecryptorAES session_aes_dec;
} ServerConnection;
Result(ServerConnection*) ServerConnection_open(ClientCredential* client_credential, EndpointIPv4 server_end, str server_key);
void ServerConnection_close(ServerConnection* conn);

View File

@ -3,11 +3,12 @@
//TODO: use AES CTR encryption instead of my own padding algorithm
void EncryptorAES_init(EncryptorAES* ptr, Array(u8) key){
void EncryptorAES_construct(EncryptorAES* ptr, Array(u8) key){
assert(key.size == 16 || key.size == 24 || key.size == 32);
br_aes_ct64_cbcenc_init(&ptr->enc_ctx, key.data, key.size);
rng_init_sha256_seedFromTime(&br_hmac_drbg_vtable, &ptr->rng_ctx.vtable);
ptr->rng_ctx.vtable = &br_hmac_drbg_vtable;
rng_init_sha256_seedFromTime(&ptr->rng_ctx.vtable);
memset(ptr->buf, 0, __AES_BUFFER_SIZE);
memset(ptr->iv, 0, sizeof(ptr->iv));
@ -49,7 +50,7 @@ void EncryptorAES_encrypt(EncryptorAES* ptr, Array(u8) src, Array(u8) dst){
}
void DecryptorAES_init(DecryptorAES* ptr, Array(u8) key){
void DecryptorAES_construct(DecryptorAES* ptr, Array(u8) key){
assert(key.size == 16 || key.size == 24 || key.size == 32);
br_aes_ct64_cbcdec_init(&ptr->dec_ctx, key.data, key.size);

View File

@ -6,14 +6,32 @@
// https://crypto.stackexchange.com/questions/3110/impacts-of-not-using-rsa-exponent-of-65537
#define DEFAULT_PUBLIC_EXPONENT 65537
bool RSA_generateKeyPair(u32 key_size, br_rsa_private_key* sk, br_rsa_public_key* pk){
br_hmac_drbg_context rng_ctx;
const br_prng_class** rng_class_ptr = &rng_ctx.vtable;
rng_init_sha256_seedFromTime(&br_hmac_drbg_vtable, rng_class_ptr);
Result(void) RSA_generateKeyPair(u32 key_size,
br_rsa_private_key* sk, br_rsa_public_key* pk,
const br_prng_class** rng_vtable_ptr)
{
Deferral(16);
bool success = false;
rng_init_sha256_seedFromTime(rng_vtable_ptr);
void* sk_buf = malloc(BR_RSA_KBUF_PRIV_SIZE(key_size));
Defer(
if(!success)
free(sk_buf)
);
void* pk_buf = malloc(BR_RSA_KBUF_PUB_SIZE(key_size));
u32 r = br_rsa_i31_keygen(rng_class_ptr, sk, sk_buf, pk, pk_buf, key_size, DEFAULT_PUBLIC_EXPONENT);
return r;
Defer(
if(!success)
free(pk_buf)
);
success = br_rsa_i31_keygen(rng_vtable_ptr, sk, sk_buf, pk, pk_buf, key_size, DEFAULT_PUBLIC_EXPONENT);
if(!success){
Return RESULT_ERROR("br_rsa_i31_keygen() failed", false);
}
Return RESULT_VOID;
}
Result(void) RSA_computePublicKey(const br_rsa_private_key* sk, br_rsa_public_key* pk){
@ -162,7 +180,8 @@ Result(void) RSA_parsePrivateKey_DER(Array(u8) _src, br_rsa_private_key* sk){
void EncryptorRSA_construct(EncryptorRSA* ptr, const br_rsa_public_key* pk){
ptr->pk = pk;
rng_init_sha256_seedFromTime(&br_hmac_drbg_vtable, &ptr->rng.vtable);
ptr->rng.vtable = &br_hmac_drbg_vtable;
rng_init_sha256_seedFromTime(&ptr->rng.vtable);
}
void EncryptorRSA_encrypt(EncryptorRSA* ptr, Array(u8) src, Array(u8) dst, u32* encrypted_size){

View File

@ -15,7 +15,7 @@
/// @param password some byte array
/// @param out_buffer u8[password_hash_size]
/// @param iterations number of iterations
void hash_password(str password, u8* out_buffer, i32 iterations);
void hash_password(Array(u8) password, u8* out_buffer, i32 iterations);
#define password_hash_size 32
@ -24,14 +24,13 @@ void hash_password(str password, u8* out_buffer, i32 iterations);
//////////////////////////////////////////////////////////////////////////////
/// @brief Initialize prng context with sha256 hashing algorithm and seed from CLOCK_REALTIME.
/// @param rng_class pointer to static vtable variable
/// @param rng_ctx pointer to vtable field in prng context
/// @param rng_vtable_ptr pointer to vtable field in prng context. The field must be initialized.
/// EXAMPLE:
/// ```
/// br_hmac_drbg_context rng_ctx;
/// rng_init_sha256_seedFromTime(&br_hmac_drbg_vtable, &rng_ctx.vtable);
/// br_hmac_drbg_context rng_ctx = { .vtable = &br_hmac_drbg_vtable };
/// rng_init_sha256_seedFromTime(&rng_ctx.vtable);
/// ```
void rng_init_sha256_seedFromTime(const br_prng_class* rng_class, const br_prng_class** rng_ctx);
void rng_init_sha256_seedFromTime(const br_prng_class** rng_vtable_ptr);
//////////////////////////////////////////////////////////////////////////////
@ -57,7 +56,7 @@ typedef struct EncryptorAES {
} EncryptorAES;
/// @param key Array<u8, 16 | 24 | 32>
void EncryptorAES_init(EncryptorAES* ptr, Array(u8) key);
void EncryptorAES_construct(EncryptorAES* ptr, Array(u8) key);
/// @brief Encrypts `src` and writes output to `dst`.
/// @param src array of any size
@ -74,7 +73,7 @@ typedef struct DecryptorAES {
} DecryptorAES;
/// @param key Array<u8, 16 | 24 | 32>
void DecryptorAES_init(DecryptorAES* ptr, Array(u8) key);
void DecryptorAES_construct(DecryptorAES* ptr, Array(u8) key);
/// @brief Decrypts `src` and writes output to `dst`.
/// @param src array of any size
@ -91,8 +90,10 @@ void DecryptorAES_decrypt(DecryptorAES* ptr, Array(u8) src, Array(u8) dst, u32*
/// @param key_size size of public key in bits (2048/3072/4096)
/// @param sk key for decryption
/// @param pk key for encryption
/// @return true on success
bool RSA_generateKeyPair(u32 key_size, br_rsa_private_key* sk, br_rsa_public_key* pk);
/// @param rng_vtable_ptr pointer to vtable field in prng context. The context must be initialized
Result(void) RSA_generateKeyPair(u32 key_size,
br_rsa_private_key* sk, br_rsa_public_key* pk,
const br_prng_class** rng_vtable_ptr);
static inline void RSA_destroyPrivateKey(br_rsa_private_key* sk){
free(sk->p);

View File

@ -2,7 +2,7 @@
#include "bearssl_hash.h"
#include "assert.h"
void hash_password(str password, u8* out_buffer, i32 iterations){
void hash_password(Array(u8) password, u8* out_buffer, i32 iterations){
assert(password_hash_size == br_sha256_SIZE);;
memset(out_buffer, 0, br_sha256_SIZE);
br_sha256_context sha256_ctx;

View File

@ -1,7 +1,8 @@
#include "cryptography.h"
#include "tlibc/time.h"
void rng_init_sha256_seedFromTime(const br_prng_class* rng_class, const br_prng_class** rng_ctx){
void rng_init_sha256_seedFromTime(const br_prng_class** rng_vtable_ptr){
nsec_t time_now = getTimeNsec();
rng_class->init(rng_ctx, &br_sha256_vtable, &time_now, sizeof(time_now));
const br_prng_class* rng_vtable = *rng_vtable_ptr;
rng_vtable->init(rng_vtable_ptr, &br_sha256_vtable, &time_now, sizeof(time_now));
}

View File

@ -1,6 +1,6 @@
#include "network/network.h"
#include "chat.h"
#include "cryptography/cryptography.h"
#include "client/client.h"
#include "server/server.h"
typedef enum ProgramMode {
Client,

View File

@ -1,6 +1,7 @@
#pragma once
#include "tlibc/std.h"
#include "tlibc/string/str.h"
#include "tlibc/errors.h"
#define port_INVALID ((u16)~0)
#define port_is_invalid(PORT) (PORT == port_INVALID)

View File

@ -1,5 +1,4 @@
#include "chat.h"
#include "network/socket.h"
#include "server.h"
#include "db/idb.h"
#include <pthread.h>

5
src/server/server.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include "network/socket.h"
#include "cryptography/cryptography.h"
Result(void) server_run(cstr server_endpoint_str);