diff --git a/native/serverconnection/src/connection/ProtocolHandler.cpp b/native/serverconnection/src/connection/ProtocolHandler.cpp index f2a70ec..f238bbb 100644 --- a/native/serverconnection/src/connection/ProtocolHandler.cpp +++ b/native/serverconnection/src/connection/ProtocolHandler.cpp @@ -57,10 +57,10 @@ void ProtocolHandler::reset() { this->crypto.initiv_command = ""; this->crypto.beta_length = 0; - if(this->crypto.identity.k) { - ecc_free(&this->crypto.identity); + if(this->crypto.identity.has_value()) { + ecc_free(&*this->crypto.identity); + this->crypto.identity = std::nullopt; } - memset(&this->crypto.identity, 0, sizeof(this->crypto.identity)); } @@ -77,6 +77,36 @@ void ProtocolHandler::reset() { this->statistics_.voice_bytes_received = 0; } +bool ProtocolHandler::initialize_identity(const std::optional &identity) { + this->reset_identity(); + assert(!this->crypto.identity.has_value()); + + this->crypto.identity.emplace(); + if(identity.has_value()) { + auto result = ecc_import((u_char*) identity.data(), (unsigned long) identity.length(), &*this->crypto.identity); + if(result == CRYPT_OK) { + return true; + } else { + this->crypto.identity.reset(); + log_error(category::connection, tr("Failed to initialize crypto identity from given parameter: {}"), result); + return false; + } + } else { + prng_state rng_state{}; + memset(&rng_state, 0, sizeof(prng_state)); + int err; + + auto result = ecc_make_key_ex(&rng_state, find_prng("sprng"), &*this->crypto.identity, <c_ecc_sets[5]); + if(result == CRYPT_OK) { + return true; + } else { + this->crypto.identity.reset(); + log_error(category::connection, tr("Failed to generate ephemeral crypto identity: {}"), result); + return false; + } + } +} + void ProtocolHandler::connect() { this->connection_state = connection_state::INIT_LOW; this->connect_timestamp = system_clock::now(); diff --git a/native/serverconnection/src/connection/ProtocolHandler.h b/native/serverconnection/src/connection/ProtocolHandler.h index f1052af..86e5070 100644 --- a/native/serverconnection/src/connection/ProtocolHandler.h +++ b/native/serverconnection/src/connection/ProtocolHandler.h @@ -76,7 +76,9 @@ namespace tc::connection { void disconnect(const std::string& /* message */); void send_acknowledge(uint16_t /* packet id */, bool /* low */); - ecc_key& get_identity_key() { return this->crypto.identity; } + /* Initialize a new crypto identity from the given string */ + bool initialize_identity(const std::optional& identity); + void reset_identity(); inline std::chrono::microseconds current_ping() const { return this->ping.value; } @@ -124,7 +126,7 @@ namespace tc::connection { uint8_t beta[54]; uint8_t beta_length; /* 10 or 54 */ - ecc_key identity{}; + std::optional identity{}; std::string initiv_command; } crypto; diff --git a/native/serverconnection/src/connection/ProtocolHandlerCrypto.cpp b/native/serverconnection/src/connection/ProtocolHandlerCrypto.cpp index bb2ed33..cd3488c 100644 --- a/native/serverconnection/src/connection/ProtocolHandlerCrypto.cpp +++ b/native/serverconnection/src/connection/ProtocolHandlerCrypto.cpp @@ -43,7 +43,7 @@ std::string ProtocolHandler::generate_client_initiv() { { size_t buffer_length = 265; char buffer[265]; - auto result = ecc_export((unsigned char *) buffer, (unsigned long*) &buffer_length, PK_PUBLIC, &this->crypto.identity); + auto result = ecc_export((unsigned char *) buffer, (unsigned long*) &buffer_length, PK_PUBLIC, &*this->crypto.identity); if(result == CRYPT_OK) { command["omega"] = base64::encode(buffer, (unsigned long) buffer_length); } else { @@ -80,7 +80,7 @@ void ProtocolHandler::handleCommandInitIVExpend(ts::Command &cmd) { } string error; - if(!this->crypt_handler.setupSharedSecret(alpha, beta, &server_key, &this->crypto.identity, error)) { + if(!this->crypt_handler.setupSharedSecret(alpha, beta, &server_key, &*this->crypto.identity, error)) { this->handle->call_connect_result.call(this->handle->errors.register_error(tr("failed to setup encryption")), true); this->handle->close_connection(); @@ -191,7 +191,7 @@ void ProtocolHandler::handleCommandInitIVExpend2(ts::Command &cmd) { memset(&prng_state, 0, sizeof(prng_state)); auto proof_data = digest::sha256(string((char*) public_key, 32) + beta); - if(ecc_sign_hash((uint8_t*) proof_data.data(), (unsigned long) proof_data.length(), (uint8_t*) sign_buffer, (unsigned long*) &sign_buffer_length, &prng_state, find_prng("sprng"), &this->crypto.identity) != CRYPT_OK) { + if(ecc_sign_hash((uint8_t*) proof_data.data(), (unsigned long) proof_data.length(), (uint8_t*) sign_buffer, (unsigned long*) &sign_buffer_length, &prng_state, find_prng("sprng"), &*this->crypto.identity) != CRYPT_OK) { this->handle->call_connect_result.call(this->handle->errors.register_error(tr("failed to generate proof of identity")), true); this->handle->close_connection(); return; diff --git a/native/serverconnection/src/connection/ServerConnection.cpp b/native/serverconnection/src/connection/ServerConnection.cpp index b266dba..eec4236 100644 --- a/native/serverconnection/src/connection/ServerConnection.cpp +++ b/native/serverconnection/src/connection/ServerConnection.cpp @@ -23,17 +23,18 @@ using namespace std; using namespace std::chrono; using namespace tc::connection; -string ErrorHandler::get_message(ErrorHandler::error_id id) { - if(id == 0) - return "success"; +string ErrorHandler::get_error_message(ErrorHandler::ErrorId id) const { + if(id == 0) { + return "success"; + } - auto index = (-id - 1) % this->error_varianz; - assert(index >= 0 && index < this->error_varianz); + auto index = (-id - 1) % this->kErrorBacklog; + assert(index >= 0 && index < this->kErrorBacklog); return this->error_messages[index]; } -ErrorHandler::error_id ErrorHandler::register_error(const string &message) { - auto index = this->error_index++ % this->error_varianz; +ErrorHandler::ErrorId ErrorHandler::register_error(const string &message) { + auto index = this->error_index++ % this->kErrorBacklog; this->error_messages[index] = message; return -index - 1; } @@ -44,8 +45,9 @@ ServerConnection::ServerConnection() { ServerConnection::~ServerConnection() { logger::debug(category::connection, tr("Begin deallocating ServerConnection {}."), (void*) this); - if(this->protocol_handler && this->protocol_handler->connection_state == connection_state::CONNECTED) - this->protocol_handler->disconnect("server connection has been destoryed"); + if(this->protocol_handler && this->protocol_handler->connection_state == connection_state::CONNECTED) { + this->protocol_handler->disconnect("server connection has been destoryed"); + } this->close_connection(); this->finalize(); @@ -114,7 +116,7 @@ void ServerConnection::initialize() { }); - this->call_connect_result = Nan::async_callback([&](ErrorHandler::error_id error_id) { + this->call_connect_result = Nan::async_callback([&](ErrorHandler::ErrorId error_id) { Nan::HandleScope scope; /* lets update the server type */ { @@ -134,7 +136,7 @@ void ServerConnection::initialize() { }); - this->call_disconnect_result = Nan::async_callback([&](ErrorHandler::error_id error_id) { + this->call_disconnect_result = Nan::async_callback([&](ErrorHandler::ErrorId error_id) { Nan::HandleScope scope; v8::Local argv[1]; argv[0] = Nan::New(error_id); @@ -274,26 +276,21 @@ NAN_METHOD(ServerConnection::connect) { this->voice_connection->reset(); this->protocol_handler->reset(); - if(identity_key->IsString()) { - auto& identity = this->protocol_handler->get_identity_key(); - std::string identity_decoded{*Nan::Utf8String(identity_key->ToString(Nan::GetCurrentContext()).ToLocalChecked())}; - auto key = base64::decode(identity_decoded); - if(ecc_import((u_char*) key.data(), (unsigned long) key.length(), &identity) != CRYPT_OK) { - Nan::ThrowError(tr("failed to import identity")); - return; - } - } else { - auto& identity = this->protocol_handler->get_identity_key(); - prng_state rndState{}; - memset(&rndState, 0, sizeof(prng_state)); - int err; - if((err = ecc_make_key_ex(&rndState, find_prng("sprng"), &identity, <c_ecc_sets[5])) != CRYPT_OK) { - Nan::ThrowError(tr("failed to generate ephemeral identity")); - return; - } + std::optional identity{std::nullopt}; + if(identity_key->IsString()) { + auto identity_encoded = Nan::Utf8String{identity_key->ToString(Nan::GetCurrentContext()).ToLocalChecked()}; + identity = std::make_optional( + base64::decode(std::string_view{*identity_encoded, (size_t) identity_encoded.length()}) + ); + } + if(!this->protocol_handler->initialize_identity(identity)) { + Nan::ThrowError(tr("failed to initialize crypto identity")); + return; + } + sockaddr_storage remote_address{}; /* resolve address */ { @@ -302,8 +299,8 @@ NAN_METHOD(ServerConnection::connect) { hints.ai_family = AF_UNSPEC; - auto _remote_host = Nan::Utf8String(remote_host->ToString(Nan::GetCurrentContext()).ToLocalChecked()); - if(getaddrinfo(*_remote_host, nullptr, &hints, &result) != 0 || !result) { + auto remote_host_ = Nan::Utf8String{remote_host->ToString(Nan::GetCurrentContext()).ToLocalChecked()}; + if(getaddrinfo(*remote_host_, nullptr, &hints, &result) != 0 || !result) { this->call_connect_result(this->errors.register_error(tr("failed to resolve hostname"))); return; } @@ -311,15 +308,21 @@ NAN_METHOD(ServerConnection::connect) { memcpy(&remote_address, result->ai_addr, result->ai_addrlen); freeaddrinfo(result); } + switch(remote_address.ss_family) { case AF_INET: ((sockaddr_in*) &remote_address)->sin_port = htons(remote_port->Int32Value(Nan::GetCurrentContext()).FromMaybe(0)); + break; + case AF_INET6: ((sockaddr_in6*) &remote_address)->sin6_port = htons(remote_port->Int32Value(Nan::GetCurrentContext()).FromMaybe(0)); - default:break; + break; + + default: + break; } - logger::info(category::connection, tr("Connecting to {}."), net::to_string(remote_address)); + log_info(category::connection, tr("Connecting to {}."), net::to_string(remote_address)); this->socket = make_shared(remote_address); if(!this->socket->initialize()) { this->call_connect_result(this->errors.register_error("failed to initialize socket")); @@ -338,8 +341,9 @@ NAN_METHOD(ServerConnection::connect) { this->close_connection(); }; - if(teamspeak->IsBoolean() && teamspeak->BooleanValue(info.GetIsolate())) - this->protocol_handler->server_type = server_type::TEAMSPEAK; + if(teamspeak->IsBoolean() && teamspeak->BooleanValue(info.GetIsolate())) { + this->protocol_handler->server_type = server_type::TEAMSPEAK; + } this->protocol_handler->connect(); } @@ -390,7 +394,9 @@ NAN_METHOD(ServerConnection::error_message) { return; } - auto error = this->errors.get_message((ErrorHandler::error_id) info[0]->IntegerValue(Nan::GetCurrentContext()).FromMaybe(0)); + auto error = this->errors.get_error_message( + (ErrorHandler::ErrorId) info[0]->IntegerValue(Nan::GetCurrentContext()).FromMaybe(0) + ); info.GetReturnValue().Set(Nan::New(error).ToLocalChecked()); } diff --git a/native/serverconnection/src/connection/ServerConnection.h b/native/serverconnection/src/connection/ServerConnection.h index 22eaf10..4feacc0 100644 --- a/native/serverconnection/src/connection/ServerConnection.h +++ b/native/serverconnection/src/connection/ServerConnection.h @@ -28,16 +28,16 @@ namespace tc { class ErrorHandler { public: - typedef int16_t error_id; - static constexpr error_id error_success = 0; - static constexpr error_id error_varianz = 5; + typedef int16_t ErrorId; + static constexpr ErrorId kErrorCodeSuccess = 0; + static constexpr ErrorId kErrorBacklog = 5; + [[nodiscard]] std::string get_error_message(ErrorId) const; + [[nodiscard]] ErrorId register_error(const std::string& /* message */); - std::array error_messages; - error_id error_index = 0; - - std::string get_message(error_id); - error_id register_error(const std::string& /* message */); + private: + std::array error_messages{}; + ErrorId error_index{0}; }; class ServerConnection : public Nan::ObjectWrap { @@ -90,8 +90,8 @@ namespace tc { std::unique_ptr callback_connect; std::unique_ptr callback_disconnect; - Nan::callback_t call_connect_result; - Nan::callback_t call_disconnect_result; + Nan::callback_t call_connect_result; + Nan::callback_t call_disconnect_result; Nan::callback_t<> execute_pending_commands; Nan::callback_t<> execute_pending_voice; Nan::callback_t execute_callback_disconnect;