From 8b003aeaa2d2f6eea381dc466cfa3b4ad6dcb1ce Mon Sep 17 00:00:00 2001 From: Ravin Perera <33562092+ravinsp@users.noreply.github.com> Date: Wed, 16 Oct 2019 06:45:49 +0530 Subject: [PATCH] Switched to binary pubkeys from base64 for internal user data (#29) * String copy optmisations. * User pubkey binary. --- src/conf.cpp | 27 +++++++++++++-------------- src/crypto.cpp | 18 +++++++++--------- src/main.cpp | 2 +- src/proc.cpp | 18 +++++++++++++----- src/proc.hpp | 2 +- src/usr/user_session_handler.cpp | 20 +++++++++++++++----- src/usr/usr.cpp | 12 ++++++------ src/usr/usr.hpp | 15 +++++++++------ src/util.cpp | 11 +++++------ 9 files changed, 72 insertions(+), 53 deletions(-) diff --git a/src/conf.cpp b/src/conf.cpp index 3658a808..58844fa8 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -263,9 +263,9 @@ int save_config() int binpair_to_b64() { if (util::base64_encode( - cfg.pubkeyb64, - reinterpret_cast(cfg.pubkey.data()), - crypto_sign_PUBLICKEYBYTES) != 0) + cfg.pubkeyb64, + reinterpret_cast(cfg.pubkey.data()), + crypto_sign_PUBLICKEYBYTES) != 0) { std::cerr << "Error encoding public key bytes.\n"; return -1; @@ -290,26 +290,25 @@ int binpair_to_b64() */ int b64pair_to_bin() { - unsigned char decoded_pubkey[crypto_sign_PUBLICKEYBYTES]; - if (util::base64_decode(decoded_pubkey, crypto_sign_PUBLICKEYBYTES, cfg.pubkeyb64) != 0) + cfg.pubkey.resize(crypto_sign_PUBLICKEYBYTES); + if (util::base64_decode( + reinterpret_cast(cfg.pubkey.data()), + cfg.pubkey.length(), cfg.pubkeyb64) != 0) { std::cerr << "Error decoding base64 public key.\n"; return -1; } - unsigned char decoded_seckey[crypto_sign_SECRETKEYBYTES]; - if (util::base64_decode(decoded_seckey, crypto_sign_SECRETKEYBYTES, cfg.seckeyb64) != 0) + cfg.seckey.resize(crypto_sign_SECRETKEYBYTES); + if (util::base64_decode( + reinterpret_cast(cfg.seckey.data()), + cfg.seckey.length(), + cfg.seckeyb64) != 0) { std::cerr << "Error decoding base64 secret key.\n"; return -1; } - - // Assign the cfg pubkey/seckey fields with the decoded strings. - - cfg.pubkey = std::string(reinterpret_cast(decoded_pubkey), crypto_sign_PUBLICKEYBYTES); - - cfg.seckey = std::string(reinterpret_cast(decoded_seckey), crypto_sign_SECRETKEYBYTES); - + return 0; } diff --git a/src/crypto.cpp b/src/crypto.cpp index 3a03a388..f0477df2 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -29,12 +29,12 @@ void generate_signing_keys(std::string &pubkey, std::string &seckey, std::string { //Generate key pair using libsodium default algorithm. (Currently using ed25519) - unsigned char pubkeychars[crypto_sign_PUBLICKEYBYTES]; - unsigned char seckeychars[crypto_sign_SECRETKEYBYTES]; - crypto_sign_keypair(pubkeychars, seckeychars); + pubkey.resize(crypto_sign_PUBLICKEYBYTES); + seckey.resize(crypto_sign_SECRETKEYBYTES); + crypto_sign_keypair( + reinterpret_cast(pubkey.data()), + reinterpret_cast(seckey.data())); - pubkey = std::string(reinterpret_cast(pubkeychars), crypto_sign_PUBLICKEYBYTES); - seckey = std::string(reinterpret_cast(seckeychars), crypto_sign_SECRETKEYBYTES); keytype = crypto_sign_primitive(); } @@ -49,15 +49,15 @@ std::string sign(std::string_view msg, std::string_view seckey) { //Generate the signature using libsodium. - unsigned char sigchars[crypto_sign_BYTES]; + std::string sig; + sig.resize(crypto_sign_BYTES); crypto_sign_detached( - sigchars, + reinterpret_cast(sig.data()), NULL, reinterpret_cast(msg.data()), msg.length(), reinterpret_cast(seckey.data())); - - std::string sig(reinterpret_cast(sigchars), crypto_sign_BYTES); + return sig; } diff --git a/src/main.cpp b/src/main.cpp index 1586c044..947623c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -113,7 +113,7 @@ int main(int argc, char **argv) { std::pair bufpair; bufpair.first = std::move(user.inbuffer); - userbufs[user.pubkeyb64] = bufpair; + userbufs[user.pubkey] = bufpair; } proc::ContractExecArgs eargs(123123345, userbufs); diff --git a/src/proc.cpp b/src/proc.cpp index 22d9365e..e5a42615 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -129,8 +129,16 @@ int write_to_stdin(const ContractExecArgs &args) if (itr != userfds.begin()) os << ","; // Trailing comma separator for previous element. - // Write user pubkey and fds. - os << "\"" << itr->first << "\":[" + // Get the base64 pubkey of the user. + std::string_view userpubkey = itr->first; // User pubkey in binary format. + std::string userpubkeyb64; + util::base64_encode( + userpubkeyb64, + reinterpret_cast(userpubkey.data()), + userpubkey.length()); + + // Write user base64 pubkey and fds. + os << "\"" << userpubkeyb64 << "\":[" << itr->second[FDTYPE::SCREAD] << "," << itr->second[FDTYPE::SCWRITE] << "]"; } @@ -209,7 +217,7 @@ int write_verified_user_inputs(const ContractExecArgs &args) if (vmsplice(writefd, memsegs, 1, 0) == -1) { std::cerr << "Error writing contract input (" << bufpair.first.length() - << " bytes) from user " << pubkey << std::endl; + << " bytes) from user" << std::endl; } // Close the writefd since we no longer need it for this round. @@ -246,7 +254,7 @@ int read_contract_user_outputs(const ContractExecArgs &args) if (bytes_available > 0) { - bufpair.second.reserve(bytes_available); // bufpair.second is the output buffer. + bufpair.second.resize(bytes_available); // bufpair.second is the output buffer. // Populate the user output buffer with new data from the pipe. // We use vmsplice to map (zero-copy) the output from the fd. @@ -261,7 +269,7 @@ int read_contract_user_outputs(const ContractExecArgs &args) } else { - std::cout << "Contract produced " << bytes_available << " bytes for user " << pubkey << std::endl; + std::cout << "Contract produced " << bytes_available << " bytes for user" << std::endl; } } diff --git a/src/proc.hpp b/src/proc.hpp index 8a302124..32a56c4a 100644 --- a/src/proc.hpp +++ b/src/proc.hpp @@ -17,7 +17,7 @@ namespace proc */ struct ContractExecArgs { - // Map of user I/O buffers (map key: user public key). + // Map of user I/O buffers (map key: user binary public key). // The value is a pair holding consensus-verified input and contract-generated output. std::unordered_map> &userbufs; diff --git a/src/usr/user_session_handler.cpp b/src/usr/user_session_handler.cpp index 79ec8899..e9eacb33 100644 --- a/src/usr/user_session_handler.cpp +++ b/src/usr/user_session_handler.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "../util.hpp" #include "../sock/socket_session.hpp" #include "usr.hpp" @@ -62,17 +63,26 @@ void user_session_handler::on_message(sock::socket_session *session, std::string { // Challenge singature verification successful. + // Decode b64 pubkey and get binary pubkey. We area only going to keep + // the binary pubkey due to reduced memory footprint. + std::string userpubkey; + userpubkey.resize(crypto_sign_PUBLICKEYBYTES); + util::base64_decode( + reinterpret_cast(userpubkey.data()), + userpubkey.length(), + userpubkeyb64); + // Now check whether this user public key is duplicate. - if (usr::sessionids.count(userpubkeyb64) == 0) + if (usr::sessionids.count(userpubkey) == 0) { // All good. Unique public key. // Promote the connection from pending-challenges to authenticated users. session->flags_.reset(util::SESSION_FLAG::USER_CHALLENGE_ISSUED); // Clear challenge-issued flag session->flags_.set(util::SESSION_FLAG::USER_AUTHED); // Set the user-authed flag - usr::add_user(session->uniqueid_, userpubkeyb64); // Add the user to the global authed user list + usr::add_user(session->uniqueid_, userpubkey); // Add the user to the global authed user list usr::pending_challenges.erase(session->uniqueid_); // Remove the stored challenge - + std::cout << "User connection " << session->uniqueid_ << " authenticated. Public key " << userpubkeyb64 << std::endl; return; @@ -100,10 +110,10 @@ void user_session_handler::on_message(sock::socket_session *session, std::string // This is an authed user. usr::connected_user &user = itr->second; - //Hand over the bytes into user inbuffer. + //Append the bytes into connected user input buffer. user.inbuffer.append(message); - std::cout << "Collected " << user.inbuffer.length() << " bytes from user " << user.pubkeyb64 << std::endl; + std::cout << "Collected " << user.inbuffer.length() << " bytes from user" << std::endl; return; } } diff --git a/src/usr/usr.cpp b/src/usr/usr.cpp index 22224d84..f51bfcb4 100644 --- a/src/usr/usr.cpp +++ b/src/usr/usr.cpp @@ -23,7 +23,7 @@ std::unordered_map users; /** * Holds set of connected user session ids for lookups. (Exposed to other sub systems) - * Map key: User pubkey + * Map key: User binary pubkey */ std::unordered_map sessionids; @@ -197,10 +197,10 @@ int verify_user_challenge_response(std::string &extracted_pubkeyb64, std::string * This should get called after the challenge handshake is verified. * * @param sessionid User socket session id. - * @param pubkeyb64 User's base64 public key. + * @param pubkey User's binary public key. * @return 0 on successful additions. -1 on failure. */ -int add_user(const std::string &sessionid, const std::string &pubkeyb64) +int add_user(const std::string &sessionid, const std::string &pubkey) { if (users.count(sessionid) == 1) { @@ -208,10 +208,10 @@ int add_user(const std::string &sessionid, const std::string &pubkeyb64) return -1; } - users.emplace(sessionid, usr::connected_user(pubkeyb64)); + users.emplace(sessionid, usr::connected_user(pubkey)); // Populate sessionid map so we can lookup by user pubkey. - sessionids.emplace(pubkeyb64, sessionid); + sessionids[pubkey] = sessionid; return 0; } @@ -235,7 +235,7 @@ int remove_user(const std::string &sessionid) usr::connected_user &user = itr->second; - sessionids.erase(user.pubkeyb64); + sessionids.erase(user.pubkey); users.erase(itr); return 0; } diff --git a/src/usr/usr.hpp b/src/usr/usr.hpp index 0192acad..2f911d7a 100644 --- a/src/usr/usr.hpp +++ b/src/usr/usr.hpp @@ -18,15 +18,18 @@ namespace usr */ struct connected_user { - // Base64 user public key - std::string pubkeyb64; - + // User binary public key + std::string pubkey; + // Holds the unprocessed user input collected from websocket. std::string inbuffer; - connected_user(std::string_view _pubkeyb64) + /** + * @param _pubkey The public key of the user in binary format. + */ + connected_user(std::string_view _pubkey) { - pubkeyb64 = _pubkeyb64; + pubkey = _pubkey; } }; @@ -54,7 +57,7 @@ void create_user_challenge(std::string &msg, std::string &challengeb64); int verify_user_challenge_response(std::string &extracted_pubkeyb64, std::string_view response, std::string_view original_challenge); -int add_user(const std::string &sessionid, const std::string &pubkeyb64); +int add_user(const std::string &sessionid, const std::string &pubkey); int remove_user(const std::string &sessionid); diff --git a/src/util.cpp b/src/util.cpp index 381beee1..1cd63d0b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -17,21 +17,20 @@ int base64_encode(std::string &encoded_string, const unsigned char *bin, size_t { // Get length of encoded result from sodium. const size_t base64_len = sodium_base64_encoded_len(bin_len, sodium_base64_VARIANT_ORIGINAL); - char base64chars[base64_len]; + + // "base64_len - 1" because sodium include '\0' in the calculated base64 length. + // Therefore we need to omit it when initializing the std::string. + encoded_string.resize(base64_len - 1); // Get encoded string. const char *encoded_str_char = sodium_bin2base64( - base64chars, base64_len, + encoded_string.data(), base64_len, bin, bin_len, sodium_base64_VARIANT_ORIGINAL); if (encoded_str_char == NULL) return -1; - // Assign the encoded char* onto the provided string reference. - // "base64_len - 1" because sodium include '\0' in the calculated base64 length. - // Therefore we need to omit it when initializing the std::string. - encoded_string = std::string(base64chars, base64_len - 1); return 0; }