From d8581f7ce9d0ecd2e6440f36694d12674e7d0008 Mon Sep 17 00:00:00 2001 From: Ravin Perera <33562092+ravinsp@users.noreply.github.com> Date: Thu, 10 Oct 2019 12:57:46 +0530 Subject: [PATCH] General code optmisations and cleanup. (#15) Updated `version_compare` based on this issue: #13 Got rid of `replace_string_contents` helper func. Replaced #define macros with static consts. Moved comments from headers to source files. --- README.md | 2 +- src/conf.cpp | 47 ++++++++++++-------- src/conf.hpp | 83 +++++++++++++++++------------------ src/crypto.cpp | 41 +++++++++++++++++- src/crypto.hpp | 41 ++---------------- src/main.cpp | 112 +++++++++++++++++++++++++++++------------------- src/proc.cpp | 18 +++++--- src/proc.hpp | 23 +++++----- src/usr/usr.cpp | 69 ++++++++++++++++++++++++----- src/usr/usr.hpp | 69 ++++------------------------- src/util.cpp | 67 ++++++++++++++++------------- src/util.hpp | 58 +++++++++---------------- 12 files changed, 331 insertions(+), 299 deletions(-) diff --git a/README.md b/README.md index 1ad23c9e..e30e48a9 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Code is divided into subsystems via namespaces. Some subsystems mentioned here a Handles contract configuration. Loads and holds the central configuration object. Used by most of the subsystems. #### crypto -Handles cryptographic activities. Wraps libsodium and offers convinience functions. +Handles cryptographic activities. Wraps libsodium and offers convenience functions. #### proc Handles contract process execution. diff --git a/src/conf.cpp b/src/conf.cpp index 3cc36d7a..bf85adf6 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -18,17 +18,16 @@ using namespace rapidjson; namespace conf { +// Global contract context struct exposed to the application. contract_ctx ctx; + +// Global configuration struct exposed to the application. contract_config cfg; -int load_config(); -int save_config(); -int validate_config(); -int validate_contract_dir_paths(); -int is_schema_valid(Document &d); -int binpair_to_b64(); -int b64pair_to_bin(); - +/** + * Loads and initializes the contract config for execution. Must be called once during application startup. + * @return 0 for success. -1 for failure. + */ int init() { // The validations/loading needs to be in this order. @@ -42,6 +41,9 @@ int init() return 0; } +/** + * Generates and saves new signing keys in the contract config. + */ int rekey() { // Load the contract config and re-save with the newly generated keys. @@ -61,6 +63,11 @@ int rekey() return 0; } +/** + * Creates a new contract directory with the default contract config. + * By the time this gets called, the 'ctx' struct must be populated. + * This function makes use of the paths populated in the ctx. + */ int create_contract() { if (boost::filesystem::exists(ctx.contractDir)) @@ -97,6 +104,10 @@ int create_contract() return 0; } +/** + * Updates the contract context with directory paths based on provided base directory. + * This is called after parsing HP command line arg in order to populate the ctx. + */ void set_contract_dir_paths(string basedir) { if (basedir[basedir.size() - 1] == '/') @@ -109,9 +120,6 @@ void set_contract_dir_paths(string basedir) ctx.stateDir = basedir + "/state"; } - -//------Private functions for this namespace (not exposed via header). - /** * Reads the config file on disk and populates the in-memory 'cfg' struct. * @@ -146,14 +154,19 @@ int load_config() } // Check whether this contract complies with the min version requirement. - string minversion = string(_HP_MIN_CONTRACT_VERSION_); - if (util::version_compare(cfgversion, minversion) == -1) + int verresult = util::version_compare(cfgversion, string(util::min_contract_version)); + if (verresult == -1) { cerr << "Contract version too old. Minimum " - << _HP_MIN_CONTRACT_VERSION_ << " required. " + << util::min_contract_version << " required. " << cfgversion << " found.\n"; return -1; } + else if (verresult == -2) + { + cerr << "Malformed version string.\n"; + return -1; + } // Load up the values into the struct. @@ -196,7 +209,7 @@ int save_config() Document d; d.SetObject(); Document::AllocatorType &allocator = d.GetAllocator(); - d.AddMember("version", StringRef(_HP_VERSION_), allocator); + d.AddMember("version", StringRef(util::hp_version), allocator); d.AddMember("pubkeyb64", StringRef(cfg.pubkeyb64.data()), allocator); d.AddMember("seckeyb64", StringRef(cfg.seckeyb64.data()), allocator); d.AddMember("binary", StringRef(cfg.binary.data()), allocator); @@ -289,8 +302,8 @@ int b64pair_to_bin() } // Assign the cfg pubkey/seckey fields with the decoded strings. - util::replace_string_contents(cfg.pubkey, (char *)decoded_pubkey, crypto_sign_PUBLICKEYBYTES); - util::replace_string_contents(cfg.seckey, (char *)decoded_seckey, crypto_sign_SECRETKEYBYTES); + cfg.pubkey = string((char *)decoded_pubkey, crypto_sign_PUBLICKEYBYTES); + cfg.seckey = string((char *)decoded_seckey, crypto_sign_SECRETKEYBYTES); return 0; } diff --git a/src/conf.hpp b/src/conf.hpp index 910c150c..5f42cbe4 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -1,29 +1,22 @@ #ifndef _HP_CONF_H_ #define _HP_CONF_H_ -// Hot Pocket version. Displayed on 'hotpocket version' and written to new contract configs. -#define _HP_VERSION_ "0.1" - -// Minimum compatible contract config version (this will be used to validate contract configs) -#define _HP_MIN_CONTRACT_VERSION_ "0.1" - -// Minimum compatible peer message version (this will be used to accept/reject incoming peer connections) -// (Keeping this as int for effcient msg payload and comparison) -#define _HP_MIN_PEERMSG_VERSION_ 1 - #include #include using namespace std; using namespace rapidjson; +/** + * Manages the central contract config and context structs. + * Contains functions to contract config operations such as create/rekey/load. + */ namespace conf { -// Holds contextual information of the currently loaded contract. +// Holds contextual information about the currently loaded contract. struct contract_ctx { - string command; // The CLI command issued to launch HotPocket string contractDir; // Contract base directory @@ -33,58 +26,62 @@ struct contract_ctx string configFile; // Full path to the contract config file }; -// +// Holds all the contract config values. struct contract_config { // Config elements which are initialized in memory (these are not directly loaded from the config file) - string pubkey; // Contract public key bytes - string seckey; // Contract secret key bytes - + string pubkey; // Contract public key bytes + string seckey; // Contract secret key bytes // Config elements which are loaded from the config file. - string pubkeyb64; // Contract base64 public key - string seckeyb64; // Contract base64 secret key - string binary; // Full path to the contract binary - string binargs; // CLI arguments to pass to the contract binary - string listenip; // The IPs to listen on for incoming connections - vector peers; // List of peers in the format ":" - vector unl; // Unique node list (list of base64 public keys) - unsigned short peerport; // Listening port for peer connections - int roundtime; // Consensus round time in ms - unsigned short pubport; // Listening port for public user connections - int pubmaxsize; // User message max size in bytes - int pubmaxcpm; // User message rate + string pubkeyb64; // Contract base64 public key + string seckeyb64; // Contract base64 secret key + string binary; // Full path to the contract binary + string binargs; // CLI arguments to pass to the contract binary + string listenip; // The IPs to listen on for incoming connections + vector peers; // List of peers in the format ":" + vector unl; // Unique node list (list of base64 public keys) + unsigned short peerport; // Listening port for peer connections + int roundtime; // Consensus round time in ms + unsigned short pubport; // Listening port for public user connections + int pubmaxsize; // User message max size in bytes + int pubmaxcpm; // User message rate }; -//Global contract context struct exposed to the application. +// Global contract context struct exposed to the application. +// Other modeuls will access context values via this. extern contract_ctx ctx; -//Global configuration struct exposed to the application. +// Global configuration struct exposed to the application. +// Other modeuls will access config values via this. extern contract_config cfg; -/** - * Loads and initializes the contract config for execution. Must be called once during application startup. - * @return 0 for success. -1 for failure. - */ int init(); -/** - * Generates and saves new signing keys in the contract config. - */ int rekey(); -/** - * Creates a new contract directory with the default contract config. - */ int create_contract(); -/** - * Updates the contract context with directory paths based on provided base directory. - */ void set_contract_dir_paths(string basedir); +//------Internal-use functions for this namespace. + +int load_config(); + +int save_config(); + +int validate_config(); + +int validate_contract_dir_paths(); + +int is_schema_valid(Document &d); + +int binpair_to_b64(); + +int b64pair_to_bin(); + } // namespace conf #endif \ No newline at end of file diff --git a/src/crypto.cpp b/src/crypto.cpp index aaf656df..0322a28d 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -9,6 +9,10 @@ using namespace std; namespace crypto { +/** + * Initializes the crypto subsystem. Must be called once during application startup. + * @return 0 for successful initialization. -1 for failure. + */ int init() { if (sodium_init() < 0) @@ -20,6 +24,9 @@ int init() return 0; } +/** + * Generates a signing key pair using libsodium and assigns them to the provided strings. + */ void generate_signing_keys(string &pubkey, string &seckey) { //Generate key pair using libsodium default algorithm. (Currently using ed25519) @@ -28,10 +35,17 @@ void generate_signing_keys(string &pubkey, string &seckey) unsigned char seckeychars[crypto_sign_SECRETKEYBYTES]; crypto_sign_keypair(pubkeychars, seckeychars); - util::replace_string_contents(pubkey, (char *)pubkeychars, crypto_sign_PUBLICKEYBYTES); - util::replace_string_contents(seckey, (char *)seckeychars, crypto_sign_SECRETKEYBYTES); + pubkey = string((char *)pubkeychars, crypto_sign_PUBLICKEYBYTES); + seckey = string((char *)seckeychars, crypto_sign_SECRETKEYBYTES); } +/** + * Returns the signature bytes for a message. + * + * @param msg Message bytes to sign. + * @param seckey Secret key bytes. + * @return Signature bytes. + */ string sign(const string &msg, const string &seckey) { //Generate the signature using libsodium. @@ -42,6 +56,13 @@ string sign(const string &msg, const string &seckey) return sig; } +/** + * Returns the base64 signature string for a message. + * + * @param msg Base64 message string to sign. + * @param seckey Base64 secret key string. + * @return Base64 signature string. + */ string sign_b64(const string &msg, const string &seckeyb64) { //Decode b64 string and generate the signature using libsodium. @@ -57,12 +78,28 @@ string sign_b64(const string &msg, const string &seckeyb64) return sigb64; } +/** + * Verifies the given signature bytes for the message. + * + * @param msg Message bytes. + * @param sig Signature bytes. + * @param pubkey Public key bytes. + * @return 0 for successful verification. -1 for failure. + */ int verify(const string &msg, const string &sig, const string &pubkey) { return crypto_sign_verify_detached( (unsigned char *)sig.data(), (unsigned char *)msg.data(), msg.length(), (unsigned char *)pubkey.data()); } +/** + * Verifies the given base64 signature for the message. + * + * @param msg Base64 message string. + * @param sig Base64 signature string. + * @param pubkey Base64 secret key. + * @return 0 for successful verification. -1 for failure. + */ int verify_b64(const string &msg, const string &sigb64, const string &pubkeyb64) { //Decode b64 string and verify the signature using libsodium. diff --git a/src/crypto.hpp b/src/crypto.hpp index 3bfb317f..53327645 100644 --- a/src/crypto.hpp +++ b/src/crypto.hpp @@ -3,56 +3,23 @@ using namespace std; +/** + * Offers convenience functions for cryptographic operations wrapping libsodium. + * These functions are used for contract config and user/peer message authentication. + */ namespace crypto { -/** - * Initializes the crypto subsystem. Must be called once during application startup. - * @return 0 for successful initialization. -1 for failure. - */ int init(); -/** - * Generates a signing key pair using libsodium and assigns them to the provided strings. - */ void generate_signing_keys(string &pubkey, string &seckey); -/** - * Returns the signature bytes for a message. - * - * @param msg Message bytes to sign. - * @param seckey Secret key bytes. - * @return Signature bytes. - */ string sign(const string &msg, const string &seckey); -/** - * Returns the base64 signature string for a message. - * - * @param msg Base64 message string to sign. - * @param seckey Base64 secret key string. - * @return Base64 signature string. - */ string sign_b64(const string &msg, const string &seckeyb64); -/** - * Verifies the given signature bytes for the message. - * - * @param msg Message bytes. - * @param sig Signature bytes. - * @param pubkey Public key bytes. - * @return 0 for successful verification. -1 for failure. - */ int verify(const string &msg, const string &sig, const string &pubkey); -/** - * Verifies the given base64 signature for the message. - * - * @param msg Base64 message string. - * @param sig Base64 signature string. - * @param pubkey Base64 secret key. - * @return 0 for successful verification. -1 for failure. - */ int verify_b64(const string &msg, const string &sigb64, const string &pubkeyb64); } // namespace crypto diff --git a/src/main.cpp b/src/main.cpp index f2d23f30..354921e8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,59 +4,27 @@ #include #include +#include "util.hpp" #include "conf.hpp" #include "crypto.hpp" #include "usr/usr.hpp" using namespace std; -int parse_cmd(int argc, char **argv); - -int main(int argc, char **argv) -{ - if (parse_cmd(argc, argv) != 0) - return -1; - - if (conf::ctx.command == "version") - { - cout << _HP_VERSION_ << endl; - } - else - { - if (crypto::init() != 0) - return -1; - - if (conf::ctx.command == "new") - { - if (conf::create_contract() != 0) - return -1; - } - else - { - if (conf::ctx.command == "rekey") - { - if (conf::rekey() != 0) - return -1; - } - else if (conf::ctx.command == "run") - { - if (conf::init() != 0 || usr::init() != 0) - return -1; - } - } - } - - cout << "exited normally\n"; - return 0; -} - +/** + * Parses CLI args and extracts hot pocket command and parameters given. + * HP command line accepts command and the contract directory(optional) + */ int parse_cmd(int argc, char **argv) { if (argc > 1) //We get working dir as an arg anyway. So we need to check for >1 args. { - string command = argv[1]; - conf::ctx.command = command; - if (command == "run" || command == "new" || command == "rekey") + // We populate the global contract ctx with the detected command. + conf::ctx.command = argv[1]; + + // For run/new/rekey, contract directory argument must be specified. + + if (conf::ctx.command == "run" || conf::ctx.command == "new" || conf::ctx.command == "rekey") { if (argc != 3) { @@ -64,18 +32,22 @@ int parse_cmd(int argc, char **argv) } else { + // We inform the conf subsystem to populate the contract directory context values + // based on the directory argument from the command line. conf::set_contract_dir_paths(argv[2]); return 0; } } - else if (command == "version") + else if (conf::ctx.command == "version") { if (argc == 2) return 0; } } + // If all extractions fail display help message. + cerr << "Arguments mismatch.\n"; cout << "Usage:\n"; cout << "hpcore version\n"; @@ -84,3 +56,55 @@ int parse_cmd(int argc, char **argv) return -1; } + +int main(int argc, char **argv) +{ + // Extract the CLI args + // After this call conf::ctx must be populated. + if (parse_cmd(argc, argv) != 0) + return -1; + + if (conf::ctx.command == "version") + { + // Print the version + cout << util::hp_version << endl; + } + else + { + // This block is about contract operations (new/rekey/run) + // All the contract operations will be executed on the contract directory specified + // in the command line args. 'parse_cmd()' above takes care of populating the contexual directory paths. + + // For any contract opreation to execute, we should init the crypto subsystem. + if (crypto::init() != 0) + return -1; + + if (conf::ctx.command == "new") + { + // This will create a new contract with all the required files. + if (conf::create_contract() != 0) + return -1; + } + else + { + if (conf::ctx.command == "rekey") + { + // This will generate new signing keys for the contract. + if (conf::rekey() != 0) + return -1; + } + else if (conf::ctx.command == "run") + { + // In order to host the contract we should init some required sub systems. + if (conf::init() != 0 || usr::init() != 0) + return -1; + + // This will start hosting the contract and start consensus rounds. + // TODO + } + } + } + + cout << "exited normally\n"; + return 0; +} diff --git a/src/proc.cpp b/src/proc.cpp index 527b419c..e47c9b59 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -19,11 +19,14 @@ namespace proc /** * Keeps the currently executing contract process id (if any) */ -int contract_pid; +int contract_pid = 0; -int write_to_stdin(ContractExecArgs &args); - -int exec_contract(ContractExecArgs &args) +/** + * Executes the contract process and passes the specified arguments. + * + * @return 0 on successful process creation. -1 on failure or contract process is already running. + */ +int exec_contract(const ContractExecArgs &args) { if (is_contract_running()) { @@ -73,7 +76,7 @@ int exec_contract(ContractExecArgs &args) * "unl":[ "pkb64", ... ] * } */ -int write_to_stdin(ContractExecArgs &args) +int write_to_stdin(const ContractExecArgs &args) { //Populate the json document with contract args. @@ -81,7 +84,7 @@ int write_to_stdin(ContractExecArgs &args) d.SetObject(); Document::AllocatorType &allocator = d.GetAllocator(); - d.AddMember("version", StringRef(_HP_VERSION_), allocator); + d.AddMember("version", StringRef(util::hp_version), allocator); d.AddMember("pubkey", StringRef(conf::cfg.pubkeyb64.data()), allocator); d.AddMember("ts", args.timestamp, allocator); @@ -137,6 +140,9 @@ int write_to_stdin(ContractExecArgs &args) return 0; } +/** + * Checks whether the contract process is running at this moment. + */ bool is_contract_running() { if (contract_pid > 0) diff --git a/src/proc.hpp b/src/proc.hpp index e0f79fe0..66c44595 100644 --- a/src/proc.hpp +++ b/src/proc.hpp @@ -8,6 +8,9 @@ using namespace std; using namespace util; +/** + * Contains helper functions regarding POSIX process execution and IPC between HP and SC. + */ namespace proc { @@ -16,9 +19,9 @@ namespace proc */ struct ContractExecArgs { - map &users; // Map of authenticated contract users indexed by user pubkey. - map &peers; // Map of connected peers indexed by node pubkey. - uint64_t timestamp; // Current HotPocket timestamp. + map &users; // Map of authenticated contract users indexed by user pubkey. + map &peers; // Map of connected peers indexed by node pubkey. + uint64_t timestamp; // Current HotPocket timestamp. ContractExecArgs(map &_users, map &_peers, uint64_t _timestamp) : users(_users), peers(_peers) @@ -27,18 +30,14 @@ struct ContractExecArgs } }; -/** - * Executes the contract process and passes the specified arguments. - * - * @return 0 on successful process creation. -1 on failure or contract process is already running. - */ -int exec_contract(ContractExecArgs &args); +int exec_contract(const ContractExecArgs &args); -/** - * Checks whether the contract process is running at this moment. - */ bool is_contract_running(); +//------Internal-use functions for this namespace. + +int write_to_stdin(const ContractExecArgs &args); + } // namespace proc #endif \ No newline at end of file diff --git a/src/usr/usr.cpp b/src/usr/usr.cpp index 84071e19..5675dd34 100644 --- a/src/usr/usr.cpp +++ b/src/usr/usr.cpp @@ -30,6 +30,10 @@ map users; */ Document challenge_response_schemadoc; +/** + * Initializes the usr subsystem. Must be called once during application startup. + * @return 0 for successful initialization. -1 for failure. + */ int init() { //We initialize the response schema doc from this json string so we can @@ -51,23 +55,37 @@ int init() return 0; } +/** + * 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 base64 challenge string into. + */ void create_user_challenge(string &msg, string &challengeb64) { //Use libsodium to generate the random challenge bytes. - unsigned char challenge_bytes[USER_CHALLENGE_LEN]; - randombytes_buf(challenge_bytes, USER_CHALLENGE_LEN); + unsigned char challenge_bytes[user_challenge_len]; + randombytes_buf(challenge_bytes, user_challenge_len); //We pass the b64 challenge string separately to the caller even though //we also include it in the challenge msg as well. - base64_encode(challenge_bytes, USER_CHALLENGE_LEN, challengeb64); + base64_encode(challenge_bytes, user_challenge_len, challengeb64); //Construct the challenge msg json. Document d; d.SetObject(); Document::AllocatorType &allocator = d.GetAllocator(); - d.AddMember("version", StringRef(_HP_VERSION_), allocator); - d.AddMember("type", MSG_PUBLIC_CHALLENGE, allocator); + d.AddMember("version", StringRef(util::hp_version), allocator); + d.AddMember("type", StringRef(msg_public_challenge), allocator); d.AddMember("challenge", StringRef(challengeb64.data()), allocator); StringBuffer buffer; @@ -76,6 +94,21 @@ void create_user_challenge(string &msg, string &challengeb64) msg = buffer.GetString(); } +/** + * Verifies the user challenge response with the original challenge issued to the user + * and the user public contained in 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 base64 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(const string &response, const string &original_challenge, string &extracted_pubkeyb64) { //We load response raw bytes into json document and validate the schema. @@ -94,16 +127,14 @@ int verify_user_challenge_response(const string &response, const string &origina } //Validate msg type. - string type = d["type"].GetString(); - if (type != MSG_CHALLENGE_RESP) + if (d["type"] != msg_challenge_resp) { cerr << "User challenge response type invalid. 'challenge_response' expeced.\n"; return -1; } //Compare the response challenge string with the original issued challenge. - string challenge = d["challenge"].GetString(); - if (challenge != original_challenge) + if (d["challenge"] != original_challenge.data()) { cerr << "User challenge resposne: challenge mismatch.\n"; return -1; @@ -121,6 +152,12 @@ int verify_user_challenge_response(const string &response, const string &origina return 0; } +/** + * Adds the specified public key into the global user list. + * This should get called after the challenge handshake is verified. + * + * @return 0 on successful additions. -1 on failure. + */ int add_user(const string &pubkeyb64) { if (users.count(pubkeyb64) == 1) @@ -156,6 +193,12 @@ int add_user(const string &pubkeyb64) return 0; } +/** + * Removes the specified public key from the global user list. + * This must get called when a user disconnects from HP. + * + * @return 0 on successful removals. -1 on failure. + */ int remove_user(const string &pubkeyb64) { if (users.count(pubkeyb64) == 0) @@ -177,6 +220,12 @@ int remove_user(const string &pubkeyb64) return 0; } +/** + * Read all per-user outputs produced by the contract process and store them in + * the user buffer for later processing. + * + * @return 0 on success. -1 on failure. + */ int read_contract_user_outputs() { //Read any outputs that has been written by the contract process @@ -199,7 +248,7 @@ int read_contract_user_outputs() read(fdout, data, bytes_available); //Populate the user output buffer with new data - util::replace_string_contents(user.outbuffer, data, bytes_available); + user.outbuffer = string(data, bytes_available); cout << "Read " + to_string(bytes_available) << " bytes into user output buffer. user:" + user.pubkeyb64 << endl; } diff --git a/src/usr/usr.hpp b/src/usr/usr.hpp index b914fda7..51743d7b 100644 --- a/src/usr/usr.hpp +++ b/src/usr/usr.hpp @@ -1,15 +1,6 @@ #ifndef _HP_USR_H_ #define _HP_USR_H_ -// Length of user random challenge bytes. -#define USER_CHALLENGE_LEN 16 - -// Message type for the user challenge. -#define MSG_PUBLIC_CHALLENGE "public_challenge" - -// Message type for the user challenge response. -#define MSG_CHALLENGE_RESP "challenge_response" - #include #include #include @@ -24,72 +15,30 @@ using namespace util; namespace usr { +// Length of user random challenge bytes. +static const int user_challenge_len = 16; + +// Message type for the user challenge. +static const char *msg_public_challenge = "public_challenge"; + +// Message type for the user challenge response. +static const char *msg_challenge_resp = "challenge_response"; + /** * Global authenticated (challenge-verified) user list. */ extern map users; -/** - * Initializes the usr subsystem. Must be called once during application startup. - * @return 0 for successful initialization. -1 for failure. - */ int init(); -/** - * 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 base64 challenge string into. - */ void create_user_challenge(string &msg, string &challengeb64); -/** - * Verifies the user challenge response with the original challenge issued to the user - * and the user public contained in 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 base64 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(const string &response, const string &original_challenge, string &extracted_pubkey); -/** - * Adds the specified public key into the global user list. - * This should get called after the challenge handshake is verified. - * - * @return 0 on successful additions. -1 on failure. - */ int add_user(const string &pubkeyb64); -/** - * Removes the specified public key from the global user list. - * This must get called when a user disconnects from HP. - * - * @return 0 on successful removals. -1 on failure. - */ int remove_user(const string &pubkeyb64); -/** - * Read all per-user outputs produced by the contract process and store them in - * the user buffer for later processing. - * - * @return 0 on success. -1 on failure. - */ int read_contract_user_outputs(); } // namespace usr diff --git a/src/util.cpp b/src/util.cpp index c3620741..5d48babb 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,18 +1,19 @@ #include #include +#include using namespace std; namespace util { -void replace_string_contents(string &str, const char *bytes, size_t bytes_len) -{ - if (str.length() > 0) - str.clear(); - str.append(bytes, bytes_len); -} - +/** + * Encodes provided bytes to base64 string. + * + * @param bin Bytes to encode. + * @param bin_len Bytes length. + * @param encoded_string String reference to assign the base64 encoded output. + */ int base64_encode(const unsigned char *bin, size_t bin_len, string &encoded_string) { // Get length of encoded result from sodium. @@ -29,10 +30,17 @@ int base64_encode(const unsigned char *bin, size_t bin_len, string &encoded_stri return -1; // Assign the encoded char* onto the provided string reference. - replace_string_contents(encoded_string, base64chars, base64_len); + encoded_string = string(base64chars, base64_len); return 0; } +/** + * Decodes provided base64 string into bytes. + * + * @param base64_str Base64 string to decode. + * @param decoded Decoded bytes. + * @param decoded_len Decoded bytes length. + */ int base64_decode(const string &base64_str, unsigned char *decoded, size_t decoded_len) { const char *b64_end; @@ -49,35 +57,34 @@ int base64_decode(const string &base64_str, unsigned char *decoded, size_t decod return 0; } -// v1 < v2 -> -1 -// v1 == v2 -> 0 -// v1 > v2 -> +1 -int version_compare(const string &v1, const string &v2) +/** + * Compare two version strings in the format of "1.12.3". + * v1 < v2 -> returns -1 + * v1 == v2 -> returns 0 + * v1 > v2 -> returns +1 + * Error -> returns -2 + */ +int version_compare(const string &x, const string &y) { - size_t i = 0, j = 0; - while (i < v1.length() || j < v2.length()) + istringstream ix(x), iy(y); + while (ix.good() || iy.good()) { - int acc1 = 0, acc2 = 0; + int cx = 0, cy = 0; + ix >> cx; + iy >> cy; - while (i < v1.length() && v1[i] != '.') - { - acc1 = acc1 * 10 + (v1[i] - '0'); - i++; - } - while (j < v2.length() && v2[j] != '.') - { - acc2 = acc2 * 10 + (v2[j] - '0'); - j++; - } + if ((!ix.eof() && !ix.good()) || (!iy.eof() && !iy.good())) + return -2; - if (acc1 < acc2) + if (cx > cy) + return 1; + if (cx < cy) return -1; - if (acc1 > acc2) - return +1; - ++i; - ++j; + ix.ignore(); + iy.ignore(); } + return 0; } diff --git a/src/util.hpp b/src/util.hpp index b7e7ce5e..0937732d 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -6,19 +6,32 @@ using namespace std; +/** + * Contains helper functions and data structures used by multiple other subsystems. + */ namespace util { +// Hot Pocket version. Displayed on 'hotpocket version' and written to new contract configs. +static const char *hp_version = "0.1"; + +// Minimum compatible contract config version (this will be used to validate contract configs) +static const char *min_contract_version = "0.1"; + +// Minimum compatible peer message version (this will be used to accept/reject incoming peer connections) +// (Keeping this as int for effcient msg payload and comparison) +static const int min_peermsg_version = 1; + /** * Holds information about an authenticated (challenge-verified) user * connected to the HotPocket node. */ struct contract_user { - string pubkeyb64; // Base64 user public key - int inpipe[2]; // Pipe to receive user input - int outpipe[2]; // Pipe to receive output produced by the contract - string outbuffer; // Holds the contract output to be processed by consensus rounds + string pubkeyb64; // Base64 user public key + int inpipe[2]; // Pipe to receive user input + int outpipe[2]; // Pipe to receive output produced by the contract + string outbuffer; // Holds the contract output to be processed by consensus rounds contract_user(const string &_pubkeyb64, int _inpipe[2], int _outpipe[2]) { @@ -35,9 +48,9 @@ struct contract_user */ struct peer_node { - string pubkeyb64; // Base64 peer public key - int inpipe[2]; // NPL pipe from HP to SC - int outpipe[2]; // NPL pipe from SC to HP + string pubkeyb64; // Base64 peer public key + int inpipe[2]; // NPL pipe from HP to SC + int outpipe[2]; // NPL pipe from SC to HP peer_node(const string &_pubkeyb64, int _inpipe[2], int _outpipe[2]) { @@ -49,41 +62,12 @@ struct peer_node } }; -/** - * Encodes provided bytes to base64 string. - * - * @param bin Bytes to encode. - * @param bin_len Bytes length. - * @param encoded_string String reference to assign the base64 encoded output. - */ int base64_encode(const unsigned char *bin, size_t bin_len, string &encoded_string); -/** - * Decodes provided base64 string into bytes. - * - * @param base64_str Base64 string to decode. - * @param decoded Decoded bytes. - * @param decoded_len Decoded bytes length. - */ int base64_decode(const string &base64_str, unsigned char *decoded, size_t decoded_len); -/** - * Replaces contents of the given string with provided bytes. - * - * @param str String reference to replace contents. - * @param bytes Bytes to write into the string. - * @param bytes_len Bytes length. - */ -void replace_string_contents(string &str, const char* bytes, size_t bytes_len); - -/** - * Compare two version strings in the format of "1.12.3". - * v1 < v2 -> returns -1 - * v1 == v2 -> returns 0 - * v1 > v2 -> returns +1 - */ int version_compare(const string &v1, const string &v2); -} // namespace usr +} // namespace util #endif \ No newline at end of file