From 98ae36ad2f0df0bbe569cf939c52ad24adf31877 Mon Sep 17 00:00:00 2001 From: Timerix Date: Sat, 13 Sep 2025 02:29:26 +0500 Subject: [PATCH] rsa key initialization --- dependencies/tlibc | 2 +- src/client.c | 10 +- src/cryptography/AES.c | 12 +-- src/cryptography/RSA.c | 164 ++++++++++++++++++++++++++++++++ src/cryptography/cryptography.h | 64 ++++++++++++- src/cryptography/rng.c | 7 ++ src/main.c | 46 +++++++++ src/server.c | 3 +- 8 files changed, 292 insertions(+), 16 deletions(-) create mode 100644 src/cryptography/RSA.c create mode 100644 src/cryptography/rng.c diff --git a/dependencies/tlibc b/dependencies/tlibc index c415e2c..7e7bd19 160000 --- a/dependencies/tlibc +++ b/dependencies/tlibc @@ -1 +1 @@ -Subproject commit c415e2ca8ff51f41984ace8fe796187e6ad0fa27 +Subproject commit 7e7bd195a9715abceb4131b21232aed7d990b75d diff --git a/src/client.c b/src/client.c index 7a14ea9..c144c94 100644 --- a/src/client.c +++ b/src/client.c @@ -77,7 +77,7 @@ Result(void) client_run() { Return RESULT_VOID; } -#define inp_eq(LITERAL) str_equals(command, STR(LITERAL)) +#define is_alias(LITERAL) str_equals(command, STR(LITERAL)) static ClientCredential* client_credential = NULL; @@ -132,7 +132,7 @@ void ClientCredential_free(ClientCredential* cred){ #define __passhash_lvl_iter 1e5 -static Result(ClientCredential*) ClientCredential_create(str username, str password){ +Result(ClientCredential*) ClientCredential_create(str username, str password){ Deferral(32); ClientCredential* cred = (ClientCredential*)malloc(sizeof(ClientCredential)); memset(cred, 0, sizeof(ClientCredential)); @@ -154,7 +154,7 @@ static Result(ClientCredential*) ClientCredential_create(str username, str passw 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); + str _passhash_lvl1_str = str_construct((void*)passhash_lvl1, password_hash_size, false); hash_password(_passhash_lvl1_str, cred->passhash_lvl2, __passhash_lvl_iter); @@ -162,7 +162,7 @@ static Result(ClientCredential*) ClientCredential_create(str username, str passw return RESULT_VALUE(p, cred); } -static void ServerConnection_close(ServerConnection* conn){ +void ServerConnection_close(ServerConnection* conn){ if(conn == NULL) return; socket_close(conn->system_socket); @@ -170,7 +170,7 @@ static void ServerConnection_close(ServerConnection* conn){ free(conn); } -static Result(ServerConnection*) connectToServer(EndpointIPv4 server_end, str server_key){ +Result(ServerConnection*) connectToServer(EndpointIPv4 server_end, str server_key){ Deferral(64); if(client_credential == NULL){ diff --git a/src/cryptography/AES.c b/src/cryptography/AES.c index 36f22a1..c6b7195 100755 --- a/src/cryptography/AES.c +++ b/src/cryptography/AES.c @@ -1,15 +1,13 @@ #include "cryptography.h" #include -#include "tlibc/time.h" -void EncryptorAES_create(EncryptorAES* ptr, Array(u8) key){ +//TODO: use AES CTR encryption instead of my own padding algorithm + +void EncryptorAES_init(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); - - // uses CLOCK_REALTIME to seed rng - nsec_t time_now = getTimeNsec(); - br_hmac_drbg_init(&ptr->rng_ctx, &br_sha256_vtable, &time_now, sizeof(time_now)); + rng_init_sha256_seedFromTime(&br_hmac_drbg_vtable, &ptr->rng_ctx.vtable); memset(ptr->buf, 0, __AES_BUFFER_SIZE); memset(ptr->iv, 0, sizeof(ptr->iv)); @@ -51,7 +49,7 @@ void EncryptorAES_encrypt(EncryptorAES* ptr, Array(u8) src, Array(u8) dst){ } -void DecryptorAES_create(DecryptorAES* ptr, Array(u8) key){ +void DecryptorAES_init(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); diff --git a/src/cryptography/RSA.c b/src/cryptography/RSA.c new file mode 100644 index 0000000..abef3e8 --- /dev/null +++ b/src/cryptography/RSA.c @@ -0,0 +1,164 @@ +#include "cryptography.h" +#include +#include "bearssl_x509.h" + +// 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, void* sk_buf, + NULLABLE(br_rsa_public_key*) pk, NULLABLE(void* pk_buf)) +{ + 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); + u32 r = br_rsa_i31_keygen(rng_class_ptr, sk, sk_buf, pk, pk_buf, key_size, DEFAULT_PUBLIC_EXPONENT); + return r; +} + +Result(void) RSA_computePublicKey(br_rsa_private_key* sk, br_rsa_public_key* pk){ + Deferral(16); + br_rsa_compute_modulus compute_modulus = br_rsa_i31_compute_modulus; + br_rsa_compute_pubexp compute_pubexp = br_rsa_i31_compute_pubexp; + + size_t modulus_size = compute_modulus(NULL, sk); + if (modulus_size == 0) { + Return RESULT_ERROR("compute_modulus", false); + } + void* modulus = malloc(modulus_size); + bool success = false; + Defer( + if(!success) + free(modulus) + ); + if (compute_modulus(modulus, sk) != modulus_size) { + Return RESULT_ERROR("compute_modulus", false); + } + + u32 pubexp_little_endian = compute_pubexp(sk); + if (pubexp_little_endian == 0) { + Return RESULT_ERROR("compute_pubexp", false); + } + u8 pubexp_big_endian[4]; + pubexp_big_endian[0] = pubexp_little_endian >> 24; + pubexp_big_endian[1] = pubexp_little_endian >> 16; + pubexp_big_endian[2] = pubexp_little_endian >> 8; + pubexp_big_endian[3] = pubexp_little_endian; + + pk->n = modulus; + pk->nlen = modulus_size; + pk->e = pubexp_big_endian; + pk->elen = sizeof pubexp_big_endian; + success = true; + Return RESULT_VOID; +} + +Result(void) RSA_serializePrivateKey_RawDER( + br_rsa_private_key* sk, + NULLABLE(br_rsa_public_key*) pk, + Array(u8)* out_der) +{ + Deferral(32); + br_rsa_compute_pubexp compute_pubexp = br_rsa_i31_compute_pubexp; + br_rsa_compute_privexp compute_privexp = br_rsa_i31_compute_privexp; + + if(pk == NULL){ + try_void(RSA_computePublicKey(sk, pk)); + Defer(free(pk->n)); + } + + u32 pubexp_little_endian = compute_pubexp(sk); + if (pubexp_little_endian == 0) { + Return RESULT_ERROR("compute_pubexp", false); + } + size_t privexp_size = compute_privexp(NULL, sk, pubexp_little_endian); + if (privexp_size == 0) { + Return RESULT_ERROR("compute_privexp", false); + } + void* privexp = malloc(privexp_size); + Defer(free(privexp)); + if (compute_privexp(privexp, sk, pubexp_little_endian) != privexp_size) { + Return RESULT_ERROR("compute_privexp", false); + } + + + size_t der_size = br_encode_rsa_raw_der(NULL, sk, pk, privexp, privexp_size); + if (der_size == 0) { + Return RESULT_ERROR("br_encode_rsa_raw_der", false); + } + void* der = malloc(der_size); + bool success = false; + Defer( + if(!success) + free(der) + ); + if (br_encode_rsa_raw_der(der, sk, pk, privexp, privexp_size) != der_size) { + Return RESULT_ERROR("br_encode_rsa_raw_der", false); + } + + success = true; + out_der->data = der; + out_der->size = der_size; + Return RESULT_VOID; +} + +void PEM_encode(Array(u8) src, Array(u8)* dst, cstr label){ + u64 encoded_size = br_pem_encode(NULL, src.data, src.size, label, 0); + // br_pem_encode doesn't count '\0' but writes it + *dst = Array_alloc_size(encoded_size + 1); + br_pem_encode(dst->data, src.data, src.size, label, 0); +} + + +Result(void) RSA_parsePrivateKey_DER(Array(u8) _src, br_rsa_private_key* sk){ + Deferral(16); + // private key data will be written in this buffer on success + Array(u8) buf = Array_copy(_src); + bool success = false; + Defer(free(buf.data)); + + br_skey_decoder_context decoder; + br_skey_decoder_init(&decoder); + br_skey_decoder_push(&decoder, buf.data, buf.size); + i32 errcode = br_skey_decoder_last_error(&decoder); + if (errcode != 0) { + Return RESULT_ERROR_FMT("br_skey_decoder error %i", errcode); + } + i32 parsed_type = br_skey_decoder_key_type(&decoder); + if(parsed_type != BR_KEYTYPE_RSA){ + Return RESULT_ERROR_FMT("parsed key has unsupported type %i", parsed_type); + } + const br_rsa_private_key* decoded_key = br_skey_decoder_get_rsa(&decoder); + if(decoded_key == NULL){ + Return RESULT_ERROR("decoder failed without errors", false); + } + + success = true; + memcpy(sk, decoded_key, sizeof(*decoded_key)); + + // sk fields still point to stack array decoder.key_data + // This code copies the data and adjusts sk fields to point to the copied chunk + Array(u8) key_data_copy = Array_alloc_size(BR_RSA_KBUF_PRIV_SIZE(decoded_key->n_bitlen)); + Defer( + if(!success) + free(key_data_copy.data); + ); + memcpy(key_data_copy.data, decoder.key_data, key_data_copy.size); + u64 memory_distance = (u64)key_data_copy.data - (u64)(void*)decoder.key_data; + u8** sk_pointer_fields[] = { + &sk->p, &sk->q, &sk->dp, &sk->dq, &sk->iq + }; + for(u32 i = 0; i < ARRAY_LEN(sk_pointer_fields); i++){ + u8** field_place = sk_pointer_fields[i]; + u64 field_value = (u64)*field_place; + field_value += memory_distance; + *field_place = (void*)field_value; + } + + Return RESULT_VOID; +} + +void EncryptorRSA_init(EncryptorRSA* ptr, Array(u8) key){ + +} + diff --git a/src/cryptography/cryptography.h b/src/cryptography/cryptography.h index 660dc8e..acde4e6 100755 --- a/src/cryptography/cryptography.h +++ b/src/cryptography/cryptography.h @@ -2,9 +2,15 @@ #include "tlibc/std.h" #include "tlibc/collections/Array.h" #include "tlibc/string/str.h" +#include "tlibc/errors.h" #include "bearssl_block.h" #include "bearssl_rand.h" #include "bearssl_rsa.h" +#include "bearssl_pem.h" + +////////////////////////////////////////////////////////////////////////////// +// hash.c // +////////////////////////////////////////////////////////////////////////////// /// @brief hashes password multiple times using its own hash as salt /// @param password some byte array @@ -14,6 +20,25 @@ void hash_password(str password, u8* out_buffer, i32 iterations); #define password_hash_size 32 +////////////////////////////////////////////////////////////////////////////// +// rng.c // +////////////////////////////////////////////////////////////////////////////// + +/// @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 +/// EXAMPLE: +/// ``` +/// br_hmac_drbg_context rng_ctx; +/// rng_init_sha256_seedFromTime(&br_hmac_drbg_vtable, &rng_ctx.vtable); +/// ``` +void rng_init_sha256_seedFromTime(const br_prng_class* rng_class, const br_prng_class** rng_ctx); + + +////////////////////////////////////////////////////////////////////////////// +// AES.c // +////////////////////////////////////////////////////////////////////////////// + typedef struct EncryptedBlockInfo { u8 padding_size; u32 _reserved; @@ -33,7 +58,7 @@ typedef struct EncryptorAES { } EncryptorAES; /// @param key Array -void EncryptorAES_create(EncryptorAES* ptr, Array(u8) key); +void EncryptorAES_init(EncryptorAES* ptr, Array(u8) key); /// @brief Encrypts `src` and writes output to `dst`. /// @param src array of any size @@ -50,7 +75,7 @@ typedef struct DecryptorAES { } DecryptorAES; /// @param key Array -void DecryptorAES_create(DecryptorAES* ptr, Array(u8) key); +void DecryptorAES_init(DecryptorAES* ptr, Array(u8) key); /// @brief Decrypts `src` and writes output to `dst`. /// @param src array of any size @@ -59,12 +84,47 @@ void DecryptorAES_create(DecryptorAES* ptr, Array(u8) key); void DecryptorAES_decrypt(DecryptorAES* ptr, Array(u8) src, Array(u8) dst, u32* decrypted_size); +////////////////////////////////////////////////////////////////////////////// +// RSA.c // +////////////////////////////////////////////////////////////////////////////// + +bool RSA_generateKeyPair(u32 key_size, + br_rsa_private_key* sk, void* sk_buf, + NULLABLE(br_rsa_public_key*) pk, NULLABLE(void* pk_buf)); + +/// @param sk some private key +/// @param pk out public key. WARNING: .n is allocated on heap +Result(void) RSA_computePublicKey(br_rsa_private_key* sk, br_rsa_public_key* pk); + +/// @brief Serialize key in raw DER binary format +/// @param sk the private key +/// @param pk set to NULL to calculate automatically +/// @param out_der out array. WARNING: .data is allocated on heap +Result(void) RSA_serializePrivateKey_RawDER( + br_rsa_private_key* sk, + NULLABLE(br_rsa_public_key*) pk, + Array(u8)* out_der); + +/// @brief Encode key data in human-readable format +/// @param src some data +/// @param dst out array. WARNING: .data is allocated on heap +/// @param label some string to write at the top of PEM file +void PEM_encode(Array(u8) src, Array(u8)* dst, cstr label); + +/// @param src serialized private key in DER format +/// @param sk out private key. WARNING: .p is allocated on heap +Result(void) RSA_parsePrivateKey_DER(Array(u8) src, br_rsa_private_key* sk); typedef struct EncryptorRSA { br_rsa_public_key public_key; } EncryptorRSA; +/// @param key Array +void EncryptorRSA_init(EncryptorRSA* ptr, Array(u8) key); typedef struct DecryptorRSA { br_rsa_private_key private_key; } DecryptorRSA; + +/// @param key Array +void DecryptorRSA_init(DecryptorRSA* ptr, Array(u8) key); diff --git a/src/cryptography/rng.c b/src/cryptography/rng.c new file mode 100644 index 0000000..1659c7e --- /dev/null +++ b/src/cryptography/rng.c @@ -0,0 +1,7 @@ +#include "cryptography.h" +#include "tlibc/time.h" + +void rng_init_sha256_seedFromTime(const br_prng_class* rng_class, const br_prng_class** rng_ctx){ + nsec_t time_now = getTimeNsec(); + rng_class->init(rng_ctx, &br_sha256_vtable, &time_now, sizeof(time_now)); +} diff --git a/src/main.c b/src/main.c index 7386ced..8e77bce 100755 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,6 @@ #include "network/network.h" #include "chat.h" +#include "cryptography/cryptography.h" typedef enum ProgramMode { Client, @@ -11,6 +12,51 @@ typedef enum ProgramMode { int main(const int argc, cstr const* argv){ Deferral(32); + const u32 key_size = 2048; + + br_rsa_private_key sk; + const u32 sk_buf_size = BR_RSA_KBUF_PRIV_SIZE(key_size); + u8 sk_buf[sk_buf_size]; + memset(sk_buf, 0, sk_buf_size); + + br_rsa_public_key pk; + const u32 pk_buf_size = BR_RSA_KBUF_PUB_SIZE(key_size); + u8 pk_buf[pk_buf_size]; + memset(pk_buf, 0, pk_buf_size); + + if(!RSA_generateKeyPair(key_size, &sk, sk_buf, &pk, pk_buf)){ + printfe("ERROR: can't generate RSA key pair\n"); + Return 1; + } + + br_rsa_public_key pk2; + try_fatal_void(RSA_computePublicKey(&sk, &pk2)); + Defer(free(pk2.n)); + if(memcmp(pk2.n, pk.n, pk.nlen) != 0){ + printfe("public key computation failed\n"); + Return 1; + } + + Array(u8) der; + try_fatal_void(RSA_serializePrivateKey_RawDER(&sk, &pk, &der)); + Defer(free(der.data)); + Array(u8) pem; + Defer(free(pem.data)); + PEM_encode(der, &pem, "RSA PRIVATE KEY"); + printf("\nserialized key:\n%s\n\n", (char*)pem.data); + + br_rsa_private_key sk2; + try_fatal_void(RSA_parsePrivateKey_DER(der, &sk2)); + Defer(free(sk2.p)); + + free(der.data); + try_fatal_void(RSA_serializePrivateKey_RawDER(&sk2, &pk, &der)); + free(pem.data); + PEM_encode(der, &pem, "RSA PRIVATE KEY"); + printf("\nparsed key:\n%s\n\n", (char*)pem.data); + + Return 0; + ProgramMode mode = Client; cstr server_endpoint_cstr; diff --git a/src/server.c b/src/server.c index d976208..4b6f0d4 100644 --- a/src/server.c +++ b/src/server.c @@ -12,7 +12,8 @@ static void* handle_connection(void* _args); Result(void) server_run(cstr server_endpoint_str){ Deferral(32); - EndpointIPv4 server_end = EndpointIPv4_fromStr(server_endpoint_str); + EndpointIPv4 server_end; + EndpointIPv4_parse(server_endpoint_str, &server_end); //TODO: add log try(Socket main_socket, i, socket_open_TCP()); try_void(socket_bind(main_socket, server_end));