#include "tlibc/tlibc.h" #include "tlibc/base64.h" #include "tlibc/filesystem.h" #include "tlibc/time.h" #include "network/network.h" #include "cli/ClientCLI/ClientCLI.h" #include "server/server_internal.h" #define _DEFAULT_CONFIG_PATH_CLIENT "tcp-chat-client.config" #define _DEFAULT_CONFIG_PATH_SERVER "tcp-chat-server.config" #define arg_is(LITERAL) str_equals(arg_str, STR(LITERAL)) typedef enum ProgramMode { ClientMode, ServerMode, RsaGenStdin, RsaGenRandom, RandomBytes, RandomBytesBase64, } ProgramMode; static void log_func(void* logger, LogSeverity severity, cstr context, cstr msg){ (void)logger; cstr severity_cstr; switch(severity){ default: severity_cstr = "INVALID_LOG_SEVERITY"; break; case LogSeverity_Debug: severity_cstr = "DBUG"; break; case LogSeverity_Info: severity_cstr = "INFO"; break; case LogSeverity_Warn: severity_cstr = "WARN"; break; case LogSeverity_Error: severity_cstr = "ERRR"; break; } DateTime dt; DateTime_getLocal(&dt); printf("[" FMT_DateTime_text "][%s/%s]: %s\n", DT_expand(dt), context, severity_cstr, msg); } int main(const int argc, cstr const* argv){ Deferral(32); try_fatal_void(tlibc_init()); Defer(tlibc_deinit()); try_fatal_void(network_init()); Defer(network_deinit()); if(br_prng_seeder_system(NULL) == NULL){ printfe("Can't get system random seeder. Bearssl is compiled incorrectly."); return 1; } ProgramMode mode = ClientMode; cstr config_path = NULL; u32 size_arg = 0; for(int argi = 1; argi < argc; argi++){ str arg_str = str_from_cstr(argv[argi]); if(arg_is("-h") || arg_is("--help")){ printf( "USAGE:\n" "no arguments Interactive client mode.\n" "-h, --help Show this message.\n" "-l, --listen Start server.\n" "--config [path] Load config from specified path.\n" " Default path for config is '" _DEFAULT_CONFIG_PATH_CLIENT "' or '" _DEFAULT_CONFIG_PATH_SERVER "'\n" "--rsa-gen-stdin [size] Generate RSA private and public keys based on stdin data (64Kb max).\n" " size: 2048 / 3072 (default) / 4096\n" " Usage: `cat somefile | tcp-chat --gen-rsa-stdin`\n" "--rsa-gen-random [size] Generate random RSA private and public keys.\n" " size: 2048 / 3072 (default) / 4096\n" "--random-bytes [size] Generate random bytes.\n" " size: any number (default=32)\n" "--random-bytes-base64 [size] Generate random bytes and print them in base64 encoding.\n" " size: any number (default=32)\n" ); Return 0; } if(arg_is("-l") || arg_is("--listen")){ if(mode != ClientMode){ printf("program mode is set already\n"); Return 1; } mode = ServerMode; } else if(arg_is("--config")){ if(++argi >= argc){ printfe("ERROR: no config path specified\n"); Return 1; } config_path = argv[argi]; } else if(arg_is("--rsa-gen-stdin")){ if(mode != ClientMode){ printf("program mode is set already\n"); Return 1; } mode = RsaGenStdin; if(++argi >= argc){ size_arg = RSA_DEFAULT_KEY_SIZE; } else if(sscanf(argv[argi], "%u", &size_arg) != 1){ printfe("ERROR: no key size specified\n"); } } else if(arg_is("--rsa-gen-random")){ if(mode != ClientMode){ printf("program mode is set already\n"); Return 1; } mode = RsaGenRandom; if(++argi >= argc){ size_arg = RSA_DEFAULT_KEY_SIZE; } else if(sscanf(argv[argi], "%u", &size_arg) != 1){ printfe("ERROR: no key size specified\n"); } } else if(arg_is("--random-bytes")){ if(mode != ClientMode){ printf("program mode is set already\n"); Return 1; } mode = RandomBytes; if(++argi >= argc){ size_arg = 32; } else if(sscanf(argv[argi], "%u", &size_arg) != 1){ printfe("ERROR: no size specified\n"); } } else if(arg_is("--random-bytes-base64")){ if(mode != ClientMode){ printf("program mode is set already\n"); Return 1; } mode = RandomBytesBase64; if(++argi >= argc){ size_arg = 32; } else if(sscanf(argv[argi], "%u", &size_arg) != 1){ printfe("ERROR: no size specified\n"); } } else { printfe("ERROR: unknown argument '%s'\n" "Use '-h' to see list of avaliable arguments\n", argv[argi]); Return 1; } } switch(mode){ case ClientMode: { if(!config_path) config_path = _DEFAULT_CONFIG_PATH_CLIENT; ClientCLI client; ClientCLI_construct(&client); Defer(ClientCLI_destroy(&client)); try_fatal_void(ClientCLI_run(&client)); break; } case ServerMode: { if(!config_path) config_path = _DEFAULT_CONFIG_PATH_SERVER; // open file try_fatal(FILE* config_file, p, file_open(config_path, FO_ReadExisting)); Defer(file_close(config_file)); // read whole file into str Array(u8) config_buf = Array_null; try_fatal_void(file_readWhole(config_file, &config_buf)); Defer(Array_free(config_buf)); str config_str = Array_castTo_str(config_buf, false); config_buf.data = NULL; // init server try_fatal(Server* server, p, Server_create(config_str, NULL, log_func)); Defer(Server_free(server)); // manually close file and free config_buf file_close(config_file); config_file = NULL; Array_free(config_buf); // start infinite loop on main thread try_fatal_void(Server_run(server)); break; } case RsaGenStdin: { printfe("reading stdin...\n"); Array(u8) input_buf = Array_alloc_size(64*1024); Defer(Array_free(input_buf)); br_hmac_drbg_context rng = { .vtable = &br_hmac_drbg_vtable }; br_hmac_drbg_init(&rng, &br_sha256_vtable, NULL, 0); i64 read_n = 0; do { read_n = fread(input_buf.data, 1, input_buf.size, stdin); if(read_n < 0){ printfe("ERROR: no input\n"); Return 1; } // put bytes to rng as seed br_hmac_drbg_update(&rng, input_buf.data, read_n); } while(read_n == input_buf.size); printfe("generating RSA key pair based on stdin...\n"); br_rsa_private_key sk; br_rsa_public_key pk; try_fatal_void(RSA_generateKeyPair(size_arg, &sk, &pk, &rng.vtable)); Defer( RSA_destroyPrivateKey(&sk); RSA_destroyPublicKey(&pk); ); str sk_str = RSA_serializePrivateKey_base64(&sk); printf("rsa_private_key = %s\n", sk_str.data); str_free(sk_str); str pk_str = RSA_serializePublicKey_base64(&pk); printf("\nrsa_public_key = %s\n", pk_str.data); str_free(pk_str); break; } case RsaGenRandom: { printfe("generating random RSA key pair...\n"); br_rsa_private_key sk; br_rsa_public_key pk; try_fatal_void(RSA_generateKeyPairFromSystemRandom(size_arg, &sk, &pk)); Defer( RSA_destroyPrivateKey(&sk); RSA_destroyPublicKey(&pk); ); str sk_str = RSA_serializePrivateKey_base64(&sk); printf("rsa_private_key = %s\n", sk_str.data); str_free(sk_str); str pk_str = RSA_serializePublicKey_base64(&pk); printf("\nrsa_public_key = %s\n", pk_str.data); str_free(pk_str); break; } case RandomBytes: { printfe("generating random bytes...\n"); br_hmac_drbg_context rng = { .vtable = &br_hmac_drbg_vtable }; rng_init_sha256_seedFromSystem(&rng.vtable); Array(u8) random_buf = Array_alloc_size(1024); u32 full_buffers_n = size_arg / random_buf.size; u32 remaining_n = size_arg % random_buf.size; while(full_buffers_n > 0){ full_buffers_n--; br_hmac_drbg_generate(&rng, random_buf.data, random_buf.size); fwrite(random_buf.data, 1, random_buf.size, stdout); } br_hmac_drbg_generate(&rng, random_buf.data, remaining_n); fwrite(random_buf.data, 1, remaining_n, stdout); break; } case RandomBytesBase64: { printfe("generating random bytes...\n"); br_hmac_drbg_context rng = { .vtable = &br_hmac_drbg_vtable }; rng_init_sha256_seedFromSystem(&rng.vtable); Array(u8) random_buf = Array_alloc_size(1024); Array(u8) base64_buf = Array_alloc_size(base64_encodedSize(random_buf.size)); u32 full_buffers_n = size_arg / random_buf.size; u32 remaining_n = size_arg % random_buf.size; u32 enc_size = 0; while(full_buffers_n > 0){ full_buffers_n--; br_hmac_drbg_generate(&rng, random_buf.data, random_buf.size); enc_size = base64_encode(random_buf.data, random_buf.size, base64_buf.data); fwrite(base64_buf.data, 1, enc_size, stdout); } br_hmac_drbg_generate(&rng, random_buf.data, remaining_n); enc_size = base64_encode(random_buf.data, remaining_n, base64_buf.data); fwrite(base64_buf.data, 1, enc_size, stdout); break; } default: printfe("ERROR: invalid program mode %i\n", mode); Return 1; } Return 0; }