diff --git a/dependencies/tlibc b/dependencies/tlibc index bf56984..c415e2c 160000 --- a/dependencies/tlibc +++ b/dependencies/tlibc @@ -1 +1 @@ -Subproject commit bf56984482d83d1a178f4da6483fbd350457e438 +Subproject commit c415e2ca8ff51f41984ace8fe796187e6ad0fa27 diff --git a/src/server.h b/src/chat.h similarity index 77% rename from src/server.h rename to src/chat.h index 05deac6..4dedced 100644 --- a/src/server.h +++ b/src/chat.h @@ -1,4 +1,5 @@ #pragma once #include "tlibc/errors.h" +Result(void) client_run(); Result(void) server_run(cstr server_endpoint_str); diff --git a/src/client.c b/src/client.c index a283db5..7a14ea9 100644 --- a/src/client.c +++ b/src/client.c @@ -1,9 +1,10 @@ -#include "client.h" +#include "chat.h" #include #include #include "term.h" - -#define inp_eq(LITERAL) str_equals(input, STR(LITERAL)) +#include "network/socket.h" +#include "cryptography/cryptography.h" +#include "tlibc/string/StringBuilder.h" static const str greeting_art = STR( " ^,,^ ╱|\n" @@ -19,8 +20,30 @@ static const str farewell_art = STR( "\\(_,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(128); + Deferral(32); if(!term_init()){ Return RESULT_ERROR("can't init terminal", false); } @@ -28,74 +51,159 @@ Result(void) client_run() { fputs(greeting_art.data, stdout); - char* input_prev = NULL; - char* input_raw = NULL; - Defer(rl_free(input_prev)); - str input = str_null; - while((input_raw = readline("> "))){ - rl_free(input_prev); - input_prev = input_raw; - input = str_from_cstr(input_raw); - line_trim(&input, true); - add_history(input.data); - if(input.size == 0){ + 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; - } - else if(inp_eq("q") || inp_eq("quit") || inp_eq("exit")){ - fputs(farewell_art.data, stdout); - break; - } - else if(inp_eq("clear")){ - term_clear(); - } - else if(inp_eq("h") || inp_eq("help")){ - - } - else if(inp_eq("c") || inp_eq("connect")){ - - } - else { - printf("ERROR: unknown kommand: '%s'\n" - "Use 'h' to see list of avaliable commands\n", - input.data); + + 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; } -void line_trim(str* line, bool set_zero_at_end){ - bool stop = false; - // loop forward - while(line->size > 0 && !stop){ - char first_char = line->data[line->size - 1]; - switch(first_char){ - case '\0': case '\r': case '\n': - case '\t': case ' ': - line->data++; - line->size--; - break; - default: - stop = true; - break; - } +#define inp_eq(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; } - // loop backward - while(line->size > 0 && !stop) - { - char last_char = line->data[line->size - 1]; - switch(last_char){ - case '\0': case '\r': case '\n': - case '\t': case ' ': - line->size--; - break; - default: - stop = true; - break; - } + else if(is_alias("clear")){ + term_clear(); } - if(set_zero_at_end){ - line->data[line->size] = '\0'; - line->isZeroTerminated = true; + 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 + +static 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(passhash_lvl1, password_hash_size, false); + hash_password(_passhash_lvl1_str, cred->passhash_lvl2, __passhash_lvl_iter); + + + success = true; + return RESULT_VALUE(p, cred); +} + +static void ServerConnection_close(ServerConnection* conn){ + if(conn == NULL) + return; + socket_close(conn->system_socket); + socket_close(conn->content_socket); + free(conn); +} + +static 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); } diff --git a/src/client.h b/src/client.h deleted file mode 100644 index 2dc7d97..0000000 --- a/src/client.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "tlibc/errors.h" - -Result(void) client_run(); - -/// @brief removes blank characters from start and end of the line -void line_trim(str* line, bool set_zero_at_end); diff --git a/src/cryptography/cryptography.h b/src/cryptography/cryptography.h index e8ea8bd..660dc8e 100755 --- a/src/cryptography/cryptography.h +++ b/src/cryptography/cryptography.h @@ -4,13 +4,14 @@ #include "tlibc/string/str.h" #include "bearssl_block.h" #include "bearssl_rand.h" +#include "bearssl_rsa.h" /// @brief hashes password multiple times using its own hash as salt /// @param password some byte array -/// @param out_buffer u8[hash_password_out_size] +/// @param out_buffer u8[password_hash_size] /// @param iterations number of iterations void hash_password(str password, u8* out_buffer, i32 iterations); -#define hash_password_out_size 32 +#define password_hash_size 32 typedef struct EncryptedBlockInfo { @@ -59,7 +60,11 @@ void DecryptorAES_decrypt(DecryptorAES* ptr, Array(u8) src, Array(u8) dst, u32* -typedef struct EncryptorRSA EncryptorRSA; +typedef struct EncryptorRSA { + br_rsa_public_key public_key; +} EncryptorRSA; -typedef struct DecryptorRSA DecryptorRSA; +typedef struct DecryptorRSA { + br_rsa_private_key private_key; +} DecryptorRSA; diff --git a/src/cryptography/hash.c b/src/cryptography/hash.c index 4327d00..ac69578 100755 --- a/src/cryptography/hash.c +++ b/src/cryptography/hash.c @@ -3,7 +3,7 @@ #include "assert.h" void hash_password(str password, u8* out_buffer, i32 iterations){ - assert(hash_password_out_size == br_sha256_SIZE);; + assert(password_hash_size == br_sha256_SIZE);; memset(out_buffer, 0, br_sha256_SIZE); br_sha256_context sha256_ctx; br_sha256_init(&sha256_ctx); @@ -11,7 +11,7 @@ void hash_password(str password, u8* out_buffer, i32 iterations){ for(i32 i = 0; i < iterations; i++){ br_sha256_update(&sha256_ctx, password.data, password.size); br_sha256_out(&sha256_ctx, out_buffer); - br_sha256_update(&sha256_ctx, out_buffer, hash_password_out_size); + br_sha256_update(&sha256_ctx, out_buffer, password_hash_size); } br_sha256_out(&sha256_ctx, out_buffer); } diff --git a/src/main.c b/src/main.c index afe535f..7386ced 100755 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,5 @@ #include "network/network.h" -#include "client.h" -#include "server.h" +#include "chat.h" typedef enum ProgramMode { Client, @@ -20,9 +19,9 @@ int main(const int argc, cstr const* argv){ if(arg_is("-h") || arg_is("--help")){ printf( "USAGE:\n" - "no arguments Interactive client mode.\n" - "-h, --help Show this message.\n" - "-l, --listen [addr:port] Start server.\n" + "no arguments Interactive client mode.\n" + "-h, --help Show this message.\n" + "-l, --listen [addr:port] Start server.\n" ); Return 0; } diff --git a/src/network/endpoint.c b/src/network/endpoint.c index 4ba20de..5ded85c 100755 --- a/src/network/endpoint.c +++ b/src/network/endpoint.c @@ -18,20 +18,42 @@ EndpointIPv4 EndpointIPv4_fromSockaddr(struct sockaddr_in saddr){ return end; } -//TODO Endpoint functions -AddressIPv4 AddressIPv4_fromStr(cstr s); - -str AddressIPv4_toStr(AddressIPv4 address); - -EndpointIPv4 EndpointIPv4_fromStr(cstr s){ - u32 a, b, c, d, p; - sscanf(s, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &p); - EndpointIPv4 e = (EndpointIPv4){ - .address = AddressIPv4_fromBytes(a, b, c, d), - .port = p - }; - return e; +Result(void) AddressIPv4_parse(cstr s, AddressIPv4* addr){ + *addr = AddressIPv4_INVALID; + u32 a, b, c, d; + if(sscanf(s, "%u.%u.%u.%u", &a, &b, &c, &d) != 4){ + return RESULT_ERROR_FMT("can't parse as AddressIPv4: '%s'", s); + } + *addr = AddressIPv4_fromBytes(a, b, c, d); + return RESULT_VOID; } -str EndpointIPv4_toStr(EndpointIPv4 end); +Result(void) EndpointIPv4_parse(cstr s, EndpointIPv4* end){ + *end = EndpointIPv4_INVALID; + u32 a, b, c, d, p; + if(sscanf(s, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &p) != 5){ + return RESULT_ERROR_FMT("can't parse as EndpointIPv4: '%s'", s); + } + *end = EndpointIPv4_create(AddressIPv4_fromBytes(a, b, c, d), p); + return RESULT_VOID; +} + +str AddressIPv4_toStr(AddressIPv4 addr){ + char* data = malloc(16); + memset(data, 0, 16); + sprintf(data, "%u.%u.%u.%u", + addr.bytes[0], addr.bytes[1], + addr.bytes[2], addr.bytes[3]); + return str_from_cstr(data); +} + +str EndpointIPv4_toStr(EndpointIPv4 end){ + char* data = malloc(24); + memset(data, 0, 24); + sprintf(data, "%u.%u.%u.%u:%u", + end.address.bytes[0], end.address.bytes[1], + end.address.bytes[2], end.address.bytes[3], + end.port); + return str_from_cstr(data); +} diff --git a/src/network/endpoint.h b/src/network/endpoint.h index 1e26d30..075e2e4 100755 --- a/src/network/endpoint.h +++ b/src/network/endpoint.h @@ -2,7 +2,7 @@ #include "tlibc/std.h" #include "tlibc/string/str.h" -#define port_INVALID ((port)~0) +#define port_INVALID ((u16)~0) #define port_is_invalid(PORT) (PORT == port_INVALID) @@ -18,8 +18,8 @@ typedef union AddressIPv4 { #define AddressIPv4_fromBytes(A, B, C, D) ((AddressIPv4){ .bytes = {A,B,C,D} }) #define AddressIPv4_fromU32(N) ((AddressIPv4){ .UintBigEndian = N }) -AddressIPv4 AddressIPv4_fromStr(cstr s); -str AddressIPv4_toStr(AddressIPv4 address); +Result(void) AddressIPv4_parse(cstr s, AddressIPv4* addr); +str AddressIPv4_toStr(AddressIPv4 addr); typedef struct EndpointIPv4 { @@ -31,8 +31,5 @@ typedef struct EndpointIPv4 { #define EndpointIPv4_is_invalid(ENDP) (AddressIPv4_is_invalid(ENDP.address) || port_is_invalid(ENDP.port)) #define EndpointIPv4_create(ADDR, PORT) ((EndpointIPv4){ADDR, PORT}) -EndpointIPv4 EndpointIPv4_fromStr(cstr s); +Result(void) EndpointIPv4_parse(cstr s, EndpointIPv4* end); str EndpointIPv4_toStr(EndpointIPv4 end); - -struct sockaddr_in EndpointIPv4_toSockaddr(EndpointIPv4 end); -EndpointIPv4 EndpointIPv4_fromSockaddr(struct sockaddr_in saddr); diff --git a/src/network/internal.h b/src/network/internal.h index 89ecc18..0b51dba 100644 --- a/src/network/internal.h +++ b/src/network/internal.h @@ -1,5 +1,6 @@ #pragma once #include "tlibc/errors.h" +#include "endpoint.h" #if !defined(KN_USE_WINSOCK) #if defined(_WIN64) || defined(_WIN32) @@ -24,3 +25,6 @@ #define RESULT_ERROR_SOCKET() RESULT_ERROR(strerror(errno), false) #endif + +struct sockaddr_in EndpointIPv4_toSockaddr(EndpointIPv4 end); +EndpointIPv4 EndpointIPv4_fromSockaddr(struct sockaddr_in saddr); diff --git a/src/server.c b/src/server.c index bfa7423..d976208 100644 --- a/src/server.c +++ b/src/server.c @@ -1,4 +1,4 @@ -#include "server.h" +#include "chat.h" #include "network/socket.h" #include "db/idb.h" #include