From 2e96ca009f8037651e1ed7a844332a379e9f77f7 Mon Sep 17 00:00:00 2001 From: Ravin Perera <33562092+ravinsp@users.noreply.github.com> Date: Thu, 31 Oct 2019 11:57:53 +0530 Subject: [PATCH] Restructured user message handling. --- CMakeLists.txt | 1 + src/conf.cpp | 11 ++- src/jsonschema/usrmsg_helpers.cpp | 139 ++++++++++++++++++++++++++++++ src/jsonschema/usrmsg_helpers.hpp | 13 +++ src/sock/socket_session.cpp | 2 + src/usr/user_session_handler.cpp | 9 +- src/usr/usr.cpp | 129 --------------------------- src/usr/usr.hpp | 4 - 8 files changed, 164 insertions(+), 144 deletions(-) create mode 100644 src/jsonschema/usrmsg_helpers.cpp create mode 100644 src/jsonschema/usrmsg_helpers.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 67a36fba..b55cfc5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(hpcore src/hplog.cpp src/fbschema/common_helpers.cpp src/fbschema/p2pmsg_helpers.cpp + src/jsonschema/usrmsg_helpers.cpp src/sock/socket_client.cpp src/sock/socket_server.cpp src/sock/socket_session.cpp diff --git a/src/conf.cpp b/src/conf.cpp index b6382f48..6672276c 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -99,10 +99,10 @@ int create_contract() cfg.peerport = 22860; cfg.roundtime = 1000; cfg.pubport = 8080; - cfg.pubmaxsize = 65536; - cfg.pubmaxcpm = 1000; - cfg.peermaxsize = 65536; - cfg.peermaxcpm = 1000; + cfg.pubmaxsize = 0; + cfg.pubmaxcpm = 0; + cfg.peermaxsize = 0; + cfg.peermaxcpm = 0; #ifndef NDEBUG cfg.loglevel = "debug"; @@ -403,8 +403,7 @@ int validate_config() // Other required fields. if (cfg.binary.empty() || cfg.listenip.empty() || - cfg.peerport == 0 || cfg.roundtime == 0 || cfg.pubport == 0 || cfg.pubmaxsize == 0 || cfg.pubmaxcpm == 0 || cfg.peermaxsize == 0 || - cfg.peermaxcpm == 0 || cfg.loglevel.empty() || cfg.loggers.empty()) + cfg.peerport == 0 || cfg.roundtime == 0 || cfg.pubport == 0 || cfg.loglevel.empty() || cfg.loggers.empty()) { std::cout << "Required configuration fields missing at " << ctx.configFile << std::endl; return -1; diff --git a/src/jsonschema/usrmsg_helpers.cpp b/src/jsonschema/usrmsg_helpers.cpp new file mode 100644 index 00000000..f2bb19c5 --- /dev/null +++ b/src/jsonschema/usrmsg_helpers.cpp @@ -0,0 +1,139 @@ +#include +#include +#include "../util.hpp" +#include "../crypto.hpp" +#include "../hplog.hpp" +#include "usrmsg_helpers.hpp" + +namespace jsonschema::usrmsg +{ + +static const char *SCHEMA_VERSION = "0.1"; + +// These fields are used on json messages response validation. +static const char *FLD_TYPE = "type"; +static const char *FLD_CHALLENGE = "challenge"; +static const char *FLD_SIG = "sig"; +static const char *FLD_PUBKEY = "pubkey"; + +// Message types +static const char *MSGTYPE_CHALLENGE = "public_challenge"; +static const char *MSGTYPE_CHALLENGE_RESP = "challenge_response"; + +// Length of user random challenge bytes. +static const size_t CHALLENGE_LEN = 16; + +/** + * Constructs user challenge message json and the challenge string required for + * initial user challenge handshake. This gets called when a user gets establishes + * a web sockets connection to HP. + * + * @param msg String reference to copy the generated json message string into. + * Message format: + * { + * "version": "", + * "type": "public_challenge", + * "challenge": "" + * } + * @param challenge String reference to copy the generated hex challenge string into. + */ +void create_user_challenge(std::string &msg, std::string &challengehex) +{ + // Use libsodium to generate the random challenge bytes. + unsigned char challenge_bytes[CHALLENGE_LEN]; + randombytes_buf(challenge_bytes, CHALLENGE_LEN); + + // We pass the hex challenge string separately to the caller even though + // we also include it in the challenge msg as well. + + util::bin2hex(challengehex, challenge_bytes, CHALLENGE_LEN); + + // Construct the challenge msg json. + // We do not use RapidJson here in favour of performance because this is a simple json message. + + // Since we know the rough size of the challenge massage we reserve adequate amount for the holder. + // Only Hot Pocket version number is variable length. Therefore message size is roughly 95 bytes + // so allocating 128bits for heap padding. + msg.reserve(128); + msg.append("{\"version\":\"") + .append(SCHEMA_VERSION) + .append("\",\"").append(FLD_TYPE).append("\":\"").append(MSGTYPE_CHALLENGE) + .append("\",\"").append(FLD_CHALLENGE).append("\":\"").append(challengehex) + .append("\"}"); +} + +/** + * Verifies the user challenge response with the original challenge issued to the user + * and the user public key contained in the response. + * + * @param extracted_pubkeyhex The hex public key extracted from the response. + * @param response The response bytes to verify. This will be parsed as json. + * Accepted response format: + * { + * "type": "challenge_response", + * "challenge": "", + * "sig": "", + * "pubkey": "" + * } + * @param original_challenge The original hex challenge string issued to the user. + * @return 0 if challenge response is verified. -1 if challenge not met or an error occurs. + */ +int verify_user_challenge_response(std::string &extracted_pubkeyhex, std::string_view response, std::string_view original_challenge) +{ + // We load response raw bytes into json document. + rapidjson::Document d; + + // Because we project the response message directly from the binary socket buffer in a zero-copy manner, the response + // string is not null terminated. 'kParseStopWhenDoneFlag' avoids rapidjson error in this case. + d.Parse(response.data()); + if (d.HasParseError()) + { + LOG_INFO << "Challenge response json parsing failed."; + return -1; + } + + // Validate msg type. + if (!d.HasMember(FLD_TYPE) || d[FLD_TYPE] != MSGTYPE_CHALLENGE_RESP) + { + LOG_INFO << "User challenge response type invalid. 'challenge_response' expected."; + return -1; + } + + // Compare the response challenge string with the original issued challenge. + if (!d.HasMember(FLD_CHALLENGE) || d[FLD_CHALLENGE] != original_challenge.data()) + { + LOG_INFO << "User challenge response challenge invalid."; + return -1; + } + + // Check for the 'sig' field existence. + if (!d.HasMember(FLD_SIG) || !d[FLD_SIG].IsString()) + { + LOG_INFO << "User challenge response signature invalid."; + return -1; + } + + // Check for the 'pubkey' field existence. + if (!d.HasMember(FLD_PUBKEY) || !d[FLD_PUBKEY].IsString()) + { + LOG_INFO << "User challenge response public key invalid."; + return -1; + } + + // Verify the challenge signature. We do this last due to signature verification cost. + std::string_view pubkeysv = util::getsv(d[FLD_PUBKEY]); + if (crypto::verify_hex( + original_challenge, + util::getsv(d[FLD_SIG]), + pubkeysv) != 0) + { + LOG_INFO << "User challenge response signature verification failed."; + return -1; + } + + extracted_pubkeyhex = pubkeysv; + + return 0; +} + +} // namespace jsonschema::usrmsg \ No newline at end of file diff --git a/src/jsonschema/usrmsg_helpers.hpp b/src/jsonschema/usrmsg_helpers.hpp new file mode 100644 index 00000000..4b1c9c5d --- /dev/null +++ b/src/jsonschema/usrmsg_helpers.hpp @@ -0,0 +1,13 @@ +#ifndef _HP_JSONSCHEMA_USRMSG_HELPERS_H_ +#define _HP_JSONSCHEMA_USRMSG_HELPERS_H_ + +#include + +namespace jsonschema::usrmsg +{ + void create_user_challenge(std::string &msg, std::string &challengehex); + + int verify_user_challenge_response(std::string &extracted_pubkeyhex, std::string_view response, std::string_view original_challenge); +} + +#endif \ No newline at end of file diff --git a/src/sock/socket_session.cpp b/src/sock/socket_session.cpp index ba2bb20f..124b0651 100644 --- a/src/sock/socket_session.cpp +++ b/src/sock/socket_session.cpp @@ -215,6 +215,8 @@ void socket_session::increment(util::SESSION_THRESHOLDS threshold_type, uint6 t.timestamp = 0; t.counter_value = 0; + LOG_INFO << "Session " << this->uniqueid << " threshold exceeded. (type:" << threshold_type << " limit:" << t.threshold_limit << ")"; + // Invoke the threshold monitor so any actions will be performed. threshold_monitor(threshold_type, t.threshold_limit, this); } diff --git a/src/usr/user_session_handler.cpp b/src/usr/user_session_handler.cpp index a2208d54..ef9b39f9 100644 --- a/src/usr/user_session_handler.cpp +++ b/src/usr/user_session_handler.cpp @@ -7,14 +7,13 @@ #include "../sock/socket_session.hpp" #include "../proc.hpp" #include "../hplog.hpp" +#include "../jsonschema/usrmsg_helpers.hpp" #include "usr.hpp" #include "user_session_handler.hpp" namespace net = boost::asio; namespace beast = boost::beast; - -using tcp = net::ip::tcp; -using error = boost::system::error_code; +namespace jusrmsg = jsonschema::usrmsg; namespace usr { @@ -42,7 +41,7 @@ void user_session_handler::on_connect(sock::socket_sessioninit_uniqueid(); @@ -74,7 +73,7 @@ void user_session_handler::on_message( { std::string userpubkeyhex; std::string_view original_challenge = itr->second; - if (usr::verify_user_challenge_response(userpubkeyhex, message, original_challenge) == 0) + if (jusrmsg::verify_user_challenge_response(userpubkeyhex, message, original_challenge) == 0) { // Challenge singature verification successful. diff --git a/src/usr/usr.cpp b/src/usr/usr.cpp index 7fcd4a89..1b210077 100644 --- a/src/usr/usr.cpp +++ b/src/usr/usr.cpp @@ -1,8 +1,6 @@ #include #include #include -#include -#include #include #include "usr.hpp" #include "user_session_handler.hpp" @@ -59,20 +57,6 @@ std::thread listener_thread; */ sock::session_options sess_opts; -// Challenge response fields. -// These fields are used on challenge response validation. -static const char *CHALLENGE_RESP_TYPE = "type"; -static const char *CHALLENGE_RESP_CHALLENGE = "challenge"; -static const char *CHALLENGE_RESP_SIG = "sig"; -static const char *CHALLENGE_RESP_PUBKEY = "pubkey"; - -// Message type for the user challenge. -static const char *CHALLENGE_MSGTYPE = "public_challenge"; -// Message type for the user challenge response. -static const char *CHALLENGE_RESP_MSGTYPE = "challenge_response"; -// Length of user random challenge bytes. -static const size_t CHALLENGE_LEN = 16; - /** * Initializes the usr subsystem. Must be called once during application startup. * @return 0 for successful initialization. -1 for failure. @@ -93,119 +77,6 @@ void deinit() stop_listening(); } -/** - * Constructs user challenge message json and the challenge string required for - * initial user challenge handshake. This gets called when a user gets establishes - * a web sockets connection to HP. - * - * @param msg String reference to copy the generated json message string into. - * Message format: - * { - * "version": "", - * "type": "public_challenge", - * "challenge": "" - * } - * @param challenge String reference to copy the generated hex challenge string into. - */ -void create_user_challenge(std::string &msg, std::string &challengehex) -{ - //Use libsodium to generate the random challenge bytes. - unsigned char challenge_bytes[CHALLENGE_LEN]; - randombytes_buf(challenge_bytes, CHALLENGE_LEN); - - //We pass the hex challenge string separately to the caller even though - //we also include it in the challenge msg as well. - - util::bin2hex(challengehex, challenge_bytes, CHALLENGE_LEN); - - //Construct the challenge msg json. - // We do not use RapidJson here in favour of performance because this is a simple json message. - - // Since we know the rough size of the challenge massage we reserve adequate amount for the holder. - // Only Hot Pocket version number is variable length. Therefore message size is roughly 95 bytes - // so allocating 128bits for heap padding. - msg.reserve(128); - msg.append("{\"version\":\"") - .append(util::HP_VERSION) - .append("\",\"type\":\"public_challenge\",\"challenge\":\"") - .append(challengehex) - .append("\"}"); -} - -/** - * Verifies the user challenge response with the original challenge issued to the user - * and the user public key contained in the response. - * - * @param extracted_pubkeyhex The hex public key extracted from the response. - * @param response The response bytes to verify. This will be parsed as json. - * Accepted response format: - * { - * "type": "challenge_response", - * "challenge": "", - * "sig": "", - * "pubkey": "" - * } - * @param original_challenge The original hex challenge string issued to the user. - * @return 0 if challenge response is verified. -1 if challenge not met or an error occurs. - */ -int verify_user_challenge_response(std::string &extracted_pubkeyhex, std::string_view response, std::string_view original_challenge) -{ - // We load response raw bytes into json document. - rapidjson::Document d; - - // Because we project the response message directly from the binary socket buffer in a zero-copy manner, the response - // string is not null terminated. 'kParseStopWhenDoneFlag' avoids rapidjson error in this case. - d.Parse(response.data()); - if (d.HasParseError()) - { - LOG_INFO << "Challenge response json parsing failed."; - return -1; - } - - // Validate msg type. - if (!d.HasMember(CHALLENGE_RESP_TYPE) || d[CHALLENGE_RESP_TYPE] != CHALLENGE_RESP_MSGTYPE) - { - LOG_INFO << "User challenge response type invalid. 'challenge_response' expected."; - return -1; - } - - // Compare the response challenge string with the original issued challenge. - if (!d.HasMember(CHALLENGE_RESP_CHALLENGE) || d[CHALLENGE_RESP_CHALLENGE] != original_challenge.data()) - { - LOG_INFO << "User challenge response challenge invalid."; - return -1; - } - - // Check for the 'sig' field existence. - if (!d.HasMember(CHALLENGE_RESP_SIG) || !d[CHALLENGE_RESP_SIG].IsString()) - { - LOG_INFO << "User challenge response signature invalid."; - return -1; - } - - // Check for the 'pubkey' field existence. - if (!d.HasMember(CHALLENGE_RESP_PUBKEY) || !d[CHALLENGE_RESP_PUBKEY].IsString()) - { - LOG_INFO << "User challenge response public key invalid."; - return -1; - } - - // Verify the challenge signature. We do this last due to signature verification cost. - std::string_view pubkeysv = util::getsv(d[CHALLENGE_RESP_PUBKEY]); - if (crypto::verify_hex( - original_challenge, - util::getsv(d[CHALLENGE_RESP_SIG]), - pubkeysv) != 0) - { - LOG_INFO << "User challenge response signature verification failed."; - return -1; - } - - extracted_pubkeyhex = pubkeysv; - - return 0; -} - /** * Adds the user denoted by specified session id and public key to the global authed user list. * This should get called after the challenge handshake is verified. diff --git a/src/usr/usr.hpp b/src/usr/usr.hpp index 946b0dd5..a90611b0 100644 --- a/src/usr/usr.hpp +++ b/src/usr/usr.hpp @@ -64,10 +64,6 @@ int init(); void deinit(); -void create_user_challenge(std::string &msg, std::string &challengehex); - -int verify_user_challenge_response(std::string &extracted_pubkeyhex, std::string_view response, std::string_view original_challenge); - int add_user(sock::socket_session *session, const std::string &pubkey); int remove_user(const std::string &sessionid);