mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
Code comments and consistency improvements. (#12)
This commit is contained in:
@@ -9,9 +9,8 @@ add_executable(hpcore
|
||||
src/conf.cpp
|
||||
src/crypto.cpp
|
||||
src/proc.cpp
|
||||
src/shared.cpp
|
||||
src/usr/usr.cpp
|
||||
src/shared.cpp
|
||||
src/util.cpp
|
||||
src/p2p/message.pb.cc
|
||||
src/sock/socket_client.cpp
|
||||
src/sock/socket_server.cpp
|
||||
|
||||
@@ -38,7 +38,7 @@ Following Instructions are based on Boost [getting started](https://www.boost.or
|
||||
#### Install Protocol buffers
|
||||
Instructions are based on [this](https://github.com/protocolbuffers/protobuf/tree/master/src).
|
||||
|
||||
1. Download and extract Protobuf 3.10.0 from [here](https://github.com/protocolbuffers/protobuf/releases/tag/v3.10.0).
|
||||
1. Download and extract Protobuf 3.10.0 from [here](https://github.com/protocolbuffers/protobuf/releases/download/v3.10.0/protobuf-cpp-3.10.0.tar.gz).
|
||||
2. Navigate to the extracted Protobuf directory in a terminal.
|
||||
3. Run `./configure`
|
||||
4. Run `make && make check`
|
||||
@@ -82,8 +82,8 @@ Handles contract process execution.
|
||||
#### usr
|
||||
Handles user connections and processing of user I/O with the smart contract. Makes use of **crypto** and **sock**.
|
||||
|
||||
#### ntn
|
||||
Handles node-to-node connections and message exchange between nodes. Also handles smart contract node-party-line (npl) I/O. Makes use of **crypto** and **sock**.
|
||||
#### p2p
|
||||
Handles peer-to-peer connections and message exchange between nodes. Also handles smart contract node-party-line (npl) I/O. Makes use of **crypto** and **sock**.
|
||||
|
||||
#### cons
|
||||
Handles consensus and proposal rounds. Makes use of **usr**, **ntn** and **proc**
|
||||
|
||||
244
src/conf.cpp
244
src/conf.cpp
@@ -8,9 +8,9 @@
|
||||
#include <rapidjson/ostreamwrapper.h>
|
||||
#include <rapidjson/prettywriter.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "conf.h"
|
||||
#include "crypto.h"
|
||||
#include "shared.h"
|
||||
#include "conf.hpp"
|
||||
#include "crypto.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
@@ -18,20 +18,25 @@ using namespace rapidjson;
|
||||
namespace conf
|
||||
{
|
||||
|
||||
ContractCtx ctx;
|
||||
ContractConfig cfg;
|
||||
contract_ctx ctx;
|
||||
contract_config cfg;
|
||||
|
||||
bool validate_config();
|
||||
int load_config();
|
||||
void save_config();
|
||||
void binpair_to_b64();
|
||||
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();
|
||||
bool validate_contract_dir_paths();
|
||||
bool is_schema_valid(Document &d);
|
||||
|
||||
int init()
|
||||
{
|
||||
if (!validate_contract_dir_paths() || load_config() != 0 || !validate_config())
|
||||
// The validations/loading needs to be in this order.
|
||||
// 1. Validate contract directories
|
||||
// 2. Read and load the contract config into memory
|
||||
// 3. Validate the loaded config values
|
||||
|
||||
if (validate_contract_dir_paths() != 0 || load_config() != 0 || validate_config() != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
@@ -39,21 +44,83 @@ int init()
|
||||
|
||||
int rekey()
|
||||
{
|
||||
// Load the contract config and re-save with the newly generated keys.
|
||||
|
||||
if (load_config() != 0)
|
||||
return -1;
|
||||
|
||||
crypto::generate_signing_keys(cfg.pubkey, cfg.seckey);
|
||||
binpair_to_b64();
|
||||
if (binpair_to_b64() != 0)
|
||||
return -1;
|
||||
|
||||
save_config();
|
||||
if (save_config() != 0)
|
||||
return -1;
|
||||
|
||||
cout << "New signing keys generated at " << ctx.configFile << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int create_contract()
|
||||
{
|
||||
if (boost::filesystem::exists(ctx.contractDir))
|
||||
{
|
||||
cerr << "Contract dir already exists. Cannot create contract at the same location.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
boost::filesystem::create_directories(ctx.configDir);
|
||||
boost::filesystem::create_directories(ctx.histDir);
|
||||
boost::filesystem::create_directories(ctx.stateDir);
|
||||
|
||||
//Create config file with default settings.
|
||||
|
||||
//We populate the in-memory struct with default settings and then save it to the file.
|
||||
|
||||
crypto::generate_signing_keys(cfg.pubkey, cfg.seckey);
|
||||
if (binpair_to_b64() != 0)
|
||||
return -1;
|
||||
|
||||
cfg.listenip = "0.0.0.0";
|
||||
cfg.peerport = 22860;
|
||||
cfg.roundtime = 1000;
|
||||
cfg.pubport = 8080;
|
||||
cfg.pubmaxsize = 65536;
|
||||
cfg.pubmaxcpm = 100;
|
||||
|
||||
//Save the default settings into the config file.
|
||||
if (save_config() != 0)
|
||||
return -1;
|
||||
|
||||
cout << "Contract directory created at " << ctx.contractDir << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_contract_dir_paths(string basedir)
|
||||
{
|
||||
if (basedir[basedir.size() - 1] == '/')
|
||||
basedir = basedir.substr(0, basedir.size() - 1);
|
||||
|
||||
ctx.contractDir = basedir;
|
||||
ctx.configDir = basedir + "/cfg";
|
||||
ctx.configFile = ctx.configDir + "/hp.cfg";
|
||||
ctx.histDir = basedir + "/hist";
|
||||
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.
|
||||
*
|
||||
* @return 0 for successful loading of config. -1 for failure.
|
||||
*/
|
||||
int load_config()
|
||||
{
|
||||
// Read the file into json document object.
|
||||
|
||||
ifstream ifs(ctx.configFile);
|
||||
IStreamWrapper isw(ifs);
|
||||
|
||||
@@ -63,13 +130,14 @@ int load_config()
|
||||
cerr << "Invalid config file format. Parser error at position " << d.GetErrorOffset() << endl;
|
||||
return -1;
|
||||
}
|
||||
else if (!is_schema_valid(d))
|
||||
else if (is_schema_valid(d) != 0)
|
||||
{
|
||||
cerr << "Invalid config file format.\n";
|
||||
return -1;
|
||||
}
|
||||
ifs.close();
|
||||
|
||||
//Check contract version.
|
||||
// Check whether the contract version is specified.
|
||||
string cfgversion = d["version"].GetString();
|
||||
if (cfgversion.empty())
|
||||
{
|
||||
@@ -77,8 +145,9 @@ int load_config()
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check whether this contract complies with the min version requirement.
|
||||
string minversion = string(_HP_MIN_CONTRACT_VERSION_);
|
||||
if (shared::version_compare(cfgversion, minversion) == -1)
|
||||
if (util::version_compare(cfgversion, minversion) == -1)
|
||||
{
|
||||
cerr << "Contract version too old. Minimum "
|
||||
<< _HP_MIN_CONTRACT_VERSION_ << " required. "
|
||||
@@ -86,6 +155,8 @@ int load_config()
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Load up the values into the struct.
|
||||
|
||||
cfg.pubkeyb64 = d["pubkeyb64"].GetString();
|
||||
cfg.seckeyb64 = d["seckeyb64"].GetString();
|
||||
cfg.binary = d["binary"].GetString();
|
||||
@@ -106,14 +177,22 @@ int load_config()
|
||||
cfg.pubmaxsize = d["pubmaxsize"].GetInt();
|
||||
cfg.pubmaxcpm = d["pubmaxcpm"].GetInt();
|
||||
|
||||
// Convert the b64 keys to binary and keep for later use.
|
||||
if (b64pair_to_bin() != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void save_config()
|
||||
/**
|
||||
* Saves the current values of the 'cfg' struct into the config file.
|
||||
*
|
||||
* @return 0 for successful save. -1 for failure.
|
||||
*/
|
||||
int save_config()
|
||||
{
|
||||
// Popualte json document with 'cfg' values.
|
||||
|
||||
Document d;
|
||||
d.SetObject();
|
||||
Document::AllocatorType &allocator = d.GetAllocator();
|
||||
@@ -148,120 +227,121 @@ void save_config()
|
||||
d.AddMember("pubmaxsize", cfg.pubmaxsize, allocator);
|
||||
d.AddMember("pubmaxcpm", cfg.pubmaxcpm, allocator);
|
||||
|
||||
|
||||
// Write the json doc to file.
|
||||
|
||||
ofstream ofs(ctx.configFile);
|
||||
OStreamWrapper osw(ofs);
|
||||
|
||||
PrettyWriter<OStreamWrapper> writer(osw);
|
||||
d.Accept(writer);
|
||||
}
|
||||
|
||||
int create_contract()
|
||||
{
|
||||
if (boost::filesystem::exists(ctx.contractDir))
|
||||
if (!d.Accept(writer))
|
||||
{
|
||||
cerr << "Contract dir already exists. Cannot create contract at the same location.\n";
|
||||
cerr << "Writing to config file failed. " << ctx.configFile << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
boost::filesystem::create_directories(ctx.configDir);
|
||||
boost::filesystem::create_directories(ctx.histDir);
|
||||
boost::filesystem::create_directories(ctx.stateDir);
|
||||
|
||||
//Create config file with default settings.
|
||||
|
||||
crypto::generate_signing_keys(cfg.pubkey, cfg.seckey);
|
||||
binpair_to_b64();
|
||||
|
||||
cfg.listenip = "0.0.0.0";
|
||||
cfg.peerport = 22860;
|
||||
cfg.roundtime = 1000;
|
||||
cfg.pubport = 8080;
|
||||
cfg.pubmaxsize = 65536;
|
||||
cfg.pubmaxcpm = 100;
|
||||
save_config();
|
||||
|
||||
cout << "Contract directory created at " << ctx.contractDir << endl;
|
||||
|
||||
if (load_config() != 0)
|
||||
return -1;
|
||||
ofs.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void binpair_to_b64()
|
||||
/**
|
||||
* Decode current binary keys in 'cfg' and populate the it with base64 keys.
|
||||
*
|
||||
* @return 0 for successful conversion. -1 for failure.
|
||||
*/
|
||||
int binpair_to_b64()
|
||||
{
|
||||
shared::base64_encode((unsigned char *)cfg.pubkey.data(), crypto_sign_PUBLICKEYBYTES, cfg.pubkeyb64);
|
||||
shared::base64_encode((unsigned char *)cfg.seckey.data(), crypto_sign_SECRETKEYBYTES, cfg.seckeyb64);
|
||||
if (util::base64_encode((unsigned char *)cfg.pubkey.data(), crypto_sign_PUBLICKEYBYTES, cfg.pubkeyb64) != 0)
|
||||
{
|
||||
cerr << "Error encoding public key bytes.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (util::base64_encode((unsigned char *)cfg.seckey.data(), crypto_sign_SECRETKEYBYTES, cfg.seckeyb64) != 0)
|
||||
{
|
||||
cerr << "Error encoding secret key bytes.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode current base64 keys in 'cfg' and populate the it with binary keys.
|
||||
*
|
||||
* @return 0 for successful conversion. -1 for failure.
|
||||
*/
|
||||
int b64pair_to_bin()
|
||||
{
|
||||
unsigned char decoded_pubkey[crypto_sign_PUBLICKEYBYTES];
|
||||
unsigned char decoded_seckey[crypto_sign_SECRETKEYBYTES];
|
||||
|
||||
if (shared::base64_decode(cfg.pubkeyb64, decoded_pubkey, crypto_sign_PUBLICKEYBYTES) != 0)
|
||||
if (util::base64_decode(cfg.pubkeyb64, decoded_pubkey, crypto_sign_PUBLICKEYBYTES) != 0)
|
||||
{
|
||||
cerr << "Error decoding base64 public key.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (shared::base64_decode(cfg.seckeyb64, decoded_seckey, crypto_sign_SECRETKEYBYTES) != 0)
|
||||
if (util::base64_decode(cfg.seckeyb64, decoded_seckey, crypto_sign_SECRETKEYBYTES) != 0)
|
||||
{
|
||||
cerr << "Error decoding base64 secret key.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
shared::replace_string_contents(cfg.pubkey, (char *)decoded_pubkey, crypto_sign_PUBLICKEYBYTES);
|
||||
shared::replace_string_contents(cfg.seckey, (char *)decoded_seckey, crypto_sign_SECRETKEYBYTES);
|
||||
// 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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool validate_config()
|
||||
/**
|
||||
* Validates the 'cfg' struct for invalid values.
|
||||
*
|
||||
* @return 0 for successful validation. -1 for failure.
|
||||
*/
|
||||
int validate_config()
|
||||
{
|
||||
// Check for non-empty signing keys.
|
||||
// We also check for key pair validity as well in the below code.
|
||||
if (cfg.pubkeyb64.empty() || cfg.seckeyb64.empty())
|
||||
{
|
||||
cerr << "Signing keys missing. Run with 'rekey' to generate new keys.\n";
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
cerr << "Required configuration fields missing at " << ctx.configFile << endl;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check whether the contract binary actually exists.
|
||||
if (!boost::filesystem::exists(cfg.binary))
|
||||
{
|
||||
cerr << "Contract binary does not exist: " << cfg.binary << endl;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Sign and verify a sample to ensure we have a matching signing key pair.
|
||||
//Sign and verify a sample message to ensure we have a matching signing key pair.
|
||||
string msg = "hotpocket";
|
||||
string sigb64 = crypto::sign_b64(msg, cfg.seckeyb64);
|
||||
if (!crypto::verify_b64(msg, sigb64, cfg.pubkeyb64))
|
||||
if (crypto::verify_b64(msg, sigb64, cfg.pubkeyb64) != 0)
|
||||
{
|
||||
cerr << "Invalid signing keys. Run with 'rekey' to generate new keys.\n";
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_contract_dir_paths(string basedir)
|
||||
{
|
||||
if (basedir[basedir.size() - 1] == '/')
|
||||
basedir = basedir.substr(0, basedir.size() - 1);
|
||||
|
||||
ctx.contractDir = basedir;
|
||||
ctx.configDir = basedir + "/cfg";
|
||||
ctx.configFile = ctx.configDir + "/hp.cfg";
|
||||
ctx.histDir = basedir + "/hist";
|
||||
ctx.stateDir = basedir + "/state";
|
||||
}
|
||||
|
||||
bool validate_contract_dir_paths()
|
||||
/**
|
||||
* Checks for the existence of all contract sub directories.
|
||||
*
|
||||
* @return 0 for successful validation. -1 for failure.
|
||||
*/
|
||||
int validate_contract_dir_paths()
|
||||
{
|
||||
string dirs[4] = {ctx.contractDir, ctx.configFile, ctx.histDir, ctx.stateDir};
|
||||
|
||||
@@ -270,14 +350,19 @@ bool validate_contract_dir_paths()
|
||||
if (!boost::filesystem::exists(dir))
|
||||
{
|
||||
cerr << dir << " does not exist.\n";
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_schema_valid(Document &d)
|
||||
/**
|
||||
* Validates the config json document schema.
|
||||
*
|
||||
* @return 0 for successful validation. -1 for failure.
|
||||
*/
|
||||
int is_schema_valid(Document &d)
|
||||
{
|
||||
const char *cfg_schema =
|
||||
"{"
|
||||
@@ -312,7 +397,10 @@ bool is_schema_valid(Document &d)
|
||||
SchemaDocument schema(sd);
|
||||
|
||||
SchemaValidator validator(schema);
|
||||
return d.Accept(validator);
|
||||
if (!d.Accept(validator))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace conf
|
||||
69
src/conf.h
69
src/conf.h
@@ -1,69 +0,0 @@
|
||||
#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 <rapidjson/document.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace conf
|
||||
{
|
||||
|
||||
struct ContractCtx
|
||||
{
|
||||
string command;
|
||||
string contractDir;
|
||||
string histDir;
|
||||
string stateDir;
|
||||
string configDir;
|
||||
string configFile;
|
||||
};
|
||||
|
||||
struct ContractConfig
|
||||
{
|
||||
/*
|
||||
Config elements which are only initialized in memory (these are not loaded from the config file)
|
||||
*/
|
||||
//public key bytes
|
||||
string pubkey;
|
||||
//secret key bytes
|
||||
string seckey;
|
||||
|
||||
/*
|
||||
Config elements which are loaded from the config file.
|
||||
*/
|
||||
string pubkeyb64;
|
||||
string seckeyb64;
|
||||
string binary;
|
||||
string binargs;
|
||||
string listenip;
|
||||
vector<string> peers;
|
||||
vector<string> unl;
|
||||
unsigned short peerport;
|
||||
int roundtime;
|
||||
unsigned short pubport;
|
||||
int pubmaxsize;
|
||||
int pubmaxcpm;
|
||||
};
|
||||
|
||||
extern ContractCtx ctx;
|
||||
extern ContractConfig cfg;
|
||||
int init();
|
||||
int rekey();
|
||||
int create_contract();
|
||||
void set_contract_dir_paths(string basedir);
|
||||
int load_config();
|
||||
void save_config();
|
||||
|
||||
} // namespace conf
|
||||
|
||||
#endif
|
||||
90
src/conf.hpp
Normal file
90
src/conf.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#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 <rapidjson/document.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace conf
|
||||
{
|
||||
|
||||
// Holds contextual information of the currently loaded contract.
|
||||
struct contract_ctx
|
||||
{
|
||||
|
||||
string command; // The CLI command issued to launch HotPocket
|
||||
|
||||
string contractDir; // Contract base directory
|
||||
string histDir; // Contract history dir
|
||||
string stateDir; // Contract state dir
|
||||
string configDir; // Contract config dir
|
||||
string configFile; // Full path to the contract config file
|
||||
};
|
||||
|
||||
//
|
||||
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
|
||||
|
||||
|
||||
// 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<string> peers; // List of peers in the format "<ip address>:<port>"
|
||||
vector<string> 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.
|
||||
extern contract_ctx ctx;
|
||||
|
||||
//Global configuration struct exposed to the application.
|
||||
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);
|
||||
|
||||
} // namespace conf
|
||||
|
||||
#endif
|
||||
@@ -1,57 +1,14 @@
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <sodium.h>
|
||||
#include "crypto.h"
|
||||
#include "shared.h"
|
||||
#include "crypto.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
void generate_signing_keys();
|
||||
void binpair_to_b64();
|
||||
int b64pair_to_bin();
|
||||
|
||||
string sign(string &msg, string &seckey)
|
||||
{
|
||||
unsigned char sigchars[crypto_sign_BYTES];
|
||||
crypto_sign_detached(sigchars, NULL, (unsigned char *)msg.data(), msg.length(), (unsigned char *)seckey.data());
|
||||
string sig((char *)sigchars, crypto_sign_BYTES);
|
||||
return sig;
|
||||
}
|
||||
|
||||
string sign_b64(string &msg, string &seckeyb64)
|
||||
{
|
||||
unsigned char seckey[crypto_sign_SECRETKEYBYTES];
|
||||
shared::base64_decode(seckeyb64, seckey, crypto_sign_SECRETKEYBYTES);
|
||||
|
||||
unsigned char sig[crypto_sign_BYTES];
|
||||
crypto_sign_detached(sig, NULL, (unsigned char *)msg.data(), msg.length(), seckey);
|
||||
string sigb64;
|
||||
shared::base64_encode(sig, crypto_sign_BYTES, sigb64);
|
||||
return sigb64;
|
||||
}
|
||||
|
||||
bool verify(string &msg, string &sig, string &pubkey)
|
||||
{
|
||||
int result = crypto_sign_verify_detached(
|
||||
(unsigned char *)sig.data(), (unsigned char *)msg.data(), msg.length(), (unsigned char *)pubkey.data());
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
bool verify_b64(string &msg, string &sigb64, string &pubkeyb64)
|
||||
{
|
||||
unsigned char decoded_pubkey[crypto_sign_PUBLICKEYBYTES];
|
||||
shared::base64_decode(pubkeyb64, decoded_pubkey, crypto_sign_PUBLICKEYBYTES);
|
||||
|
||||
unsigned char decoded_sig[crypto_sign_BYTES];
|
||||
shared::base64_decode(sigb64, decoded_sig, crypto_sign_BYTES);
|
||||
|
||||
int result = crypto_sign_verify_detached(decoded_sig, (unsigned char *)msg.data(), msg.length(), decoded_pubkey);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
int init()
|
||||
{
|
||||
if (sodium_init() < 0)
|
||||
@@ -65,12 +22,58 @@ int init()
|
||||
|
||||
void generate_signing_keys(string &pubkey, string &seckey)
|
||||
{
|
||||
//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);
|
||||
|
||||
shared::replace_string_contents(pubkey, (char *)pubkeychars, crypto_sign_PUBLICKEYBYTES);
|
||||
shared::replace_string_contents(seckey, (char *)seckeychars, crypto_sign_SECRETKEYBYTES);
|
||||
util::replace_string_contents(pubkey, (char *)pubkeychars, crypto_sign_PUBLICKEYBYTES);
|
||||
util::replace_string_contents(seckey, (char *)seckeychars, crypto_sign_SECRETKEYBYTES);
|
||||
}
|
||||
|
||||
string sign(const string &msg, const string &seckey)
|
||||
{
|
||||
//Generate the signature using libsodium.
|
||||
|
||||
unsigned char sigchars[crypto_sign_BYTES];
|
||||
crypto_sign_detached(sigchars, NULL, (unsigned char *)msg.data(), msg.length(), (unsigned char *)seckey.data());
|
||||
string sig((char *)sigchars, crypto_sign_BYTES);
|
||||
return sig;
|
||||
}
|
||||
|
||||
string sign_b64(const string &msg, const string &seckeyb64)
|
||||
{
|
||||
//Decode b64 string and generate the signature using libsodium.
|
||||
|
||||
unsigned char seckey[crypto_sign_SECRETKEYBYTES];
|
||||
util::base64_decode(seckeyb64, seckey, crypto_sign_SECRETKEYBYTES);
|
||||
|
||||
unsigned char sig[crypto_sign_BYTES];
|
||||
crypto_sign_detached(sig, NULL, (unsigned char *)msg.data(), msg.length(), seckey);
|
||||
|
||||
string sigb64;
|
||||
util::base64_encode(sig, crypto_sign_BYTES, sigb64);
|
||||
return sigb64;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
int verify_b64(const string &msg, const string &sigb64, const string &pubkeyb64)
|
||||
{
|
||||
//Decode b64 string and verify the signature using libsodium.
|
||||
|
||||
unsigned char decoded_pubkey[crypto_sign_PUBLICKEYBYTES];
|
||||
util::base64_decode(pubkeyb64, decoded_pubkey, crypto_sign_PUBLICKEYBYTES);
|
||||
|
||||
unsigned char decoded_sig[crypto_sign_BYTES];
|
||||
util::base64_decode(sigb64, decoded_sig, crypto_sign_BYTES);
|
||||
|
||||
return crypto_sign_verify_detached(decoded_sig, (unsigned char *)msg.data(), msg.length(), decoded_pubkey);
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
35
src/crypto.h
35
src/crypto.h
@@ -1,35 +0,0 @@
|
||||
#ifndef _HP_CRYPTO_H_
|
||||
#define _HP_CRYPTO_H_
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
int init();
|
||||
|
||||
void generate_signing_keys(string &pubkey, string &seckey);
|
||||
|
||||
/**
|
||||
* Returns the signature bytes for the given message bytes using the provided secret key bytes.
|
||||
*/
|
||||
string sign(string &msg, string &seckey);
|
||||
|
||||
/**
|
||||
* Returns the base64 signature for the given message bytes using the provided base64 secret key.
|
||||
*/
|
||||
string sign_b64(string &msg, string &seckeyb64);
|
||||
|
||||
/**
|
||||
* Verifies the given signature bytes for the message bytes using the provided public key bytes.
|
||||
*/
|
||||
bool verify(string &msg, string &sig, string &pubkey);
|
||||
|
||||
/**
|
||||
* Verifies the given base64 signature with the message bytes using the provided base64 public key.
|
||||
*/
|
||||
bool verify_b64(string &msg, string &sigb64, string &pubkeyb64);
|
||||
|
||||
} // namespace crypto
|
||||
|
||||
#endif
|
||||
60
src/crypto.hpp
Normal file
60
src/crypto.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef _HP_CRYPTO_H_
|
||||
#define _HP_CRYPTO_H_
|
||||
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
#endif
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include "conf.h"
|
||||
#include "crypto.h"
|
||||
#include "usr/usr.h"
|
||||
#include "conf.hpp"
|
||||
#include "crypto.hpp"
|
||||
#include "usr/usr.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -82,5 +82,5 @@ int parse_cmd(int argc, char **argv)
|
||||
cout << "hpcore <command> <contract dir> (command = run | new | rekey)\n";
|
||||
cout << "Example: hpcore run ~/mycontract\n";
|
||||
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
27
src/proc.cpp
27
src/proc.cpp
@@ -7,11 +7,11 @@
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/writer.h>
|
||||
#include "proc.h"
|
||||
#include "conf.h"
|
||||
#include "proc.hpp"
|
||||
#include "conf.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace shared;
|
||||
using namespace util;
|
||||
|
||||
namespace proc
|
||||
{
|
||||
@@ -22,7 +22,6 @@ namespace proc
|
||||
int contract_pid;
|
||||
|
||||
int write_to_stdin(ContractExecArgs &args);
|
||||
bool is_contract_running();
|
||||
|
||||
int exec_contract(ContractExecArgs &args)
|
||||
{
|
||||
@@ -35,19 +34,19 @@ int exec_contract(ContractExecArgs &args)
|
||||
int pid = fork();
|
||||
if (pid > 0)
|
||||
{
|
||||
//HotPocket process.
|
||||
// HotPocket process.
|
||||
|
||||
contract_pid = pid;
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
//Contract process.
|
||||
//Set up the process environment and overlay the contract binary program with execv().
|
||||
// Contract process.
|
||||
// Set up the process environment and overlay the contract binary program with execv().
|
||||
|
||||
//Set the contract process working directory.
|
||||
// Set the contract process working directory.
|
||||
chdir(conf::ctx.contractDir.data());
|
||||
|
||||
//Write the contract input message from HotPocket to the stdin (0) of the contract process.
|
||||
// Write the contract input message from HotPocket to the stdin (0) of the contract process.
|
||||
write_to_stdin(args);
|
||||
|
||||
char *execv_args[] = {conf::cfg.binary.data(), conf::cfg.binargs.data(), NULL};
|
||||
@@ -76,6 +75,8 @@ int exec_contract(ContractExecArgs &args)
|
||||
*/
|
||||
int write_to_stdin(ContractExecArgs &args)
|
||||
{
|
||||
//Populate the json document with contract args.
|
||||
|
||||
Document d;
|
||||
d.SetObject();
|
||||
Document::AllocatorType &allocator = d.GetAllocator();
|
||||
@@ -107,13 +108,16 @@ int write_to_stdin(ContractExecArgs &args)
|
||||
Value unl(kArrayType);
|
||||
for (string &node : conf::cfg.unl)
|
||||
unl.PushBack(StringRef(node.data()), allocator);
|
||||
d.AddMember("unl", unl, allocator);
|
||||
d.AddMember("unl", unl, allocator);
|
||||
|
||||
StringBuffer buffer;
|
||||
Writer<StringBuffer> writer(buffer);
|
||||
d.Accept(writer);
|
||||
|
||||
// Get the json string that should be written to contract input pipe.
|
||||
const char *json = buffer.GetString();
|
||||
|
||||
// Establish contract input pipe.
|
||||
int stdinpipe[2];
|
||||
if (pipe(stdinpipe) != 0)
|
||||
{
|
||||
@@ -121,9 +125,12 @@ int write_to_stdin(ContractExecArgs &args)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Redirect pipe read-end to the contract std input so the
|
||||
// contract process can read from our pipe.
|
||||
dup2(stdinpipe[0], STDIN_FILENO);
|
||||
close(stdinpipe[0]);
|
||||
|
||||
// Write the json message and close write fd.
|
||||
write(stdinpipe[1], json, buffer.GetSize());
|
||||
close(stdinpipe[1]);
|
||||
|
||||
|
||||
32
src/proc.h
32
src/proc.h
@@ -1,32 +0,0 @@
|
||||
#ifndef _HP_PROC_H_
|
||||
#define _HP_PROC_H_
|
||||
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include "shared.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace shared;
|
||||
|
||||
namespace proc
|
||||
{
|
||||
|
||||
struct ContractExecArgs
|
||||
{
|
||||
map<string, ContractUser> &users;
|
||||
map<string, PeerNode> &peers;
|
||||
uint64_t timestamp;
|
||||
|
||||
ContractExecArgs(map<string, ContractUser> &_users, map<string, PeerNode> &_peers, uint64_t _timestamp)
|
||||
: users(_users), peers(_peers)
|
||||
{
|
||||
timestamp = _timestamp;
|
||||
}
|
||||
};
|
||||
|
||||
int exec_contract(ContractExecArgs &args);
|
||||
bool is_contract_running();
|
||||
|
||||
} // namespace proc
|
||||
|
||||
#endif
|
||||
44
src/proc.hpp
Normal file
44
src/proc.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef _HP_PROC_H_
|
||||
#define _HP_PROC_H_
|
||||
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include "util.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace util;
|
||||
|
||||
namespace proc
|
||||
{
|
||||
|
||||
/**
|
||||
* Holds information that should be passed into the contract process.
|
||||
*/
|
||||
struct ContractExecArgs
|
||||
{
|
||||
map<string, contract_user> &users; // Map of authenticated contract users indexed by user pubkey.
|
||||
map<string, peer_node> &peers; // Map of connected peers indexed by node pubkey.
|
||||
uint64_t timestamp; // Current HotPocket timestamp.
|
||||
|
||||
ContractExecArgs(map<string, contract_user> &_users, map<string, peer_node> &_peers, uint64_t _timestamp)
|
||||
: users(_users), peers(_peers)
|
||||
{
|
||||
timestamp = _timestamp;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Checks whether the contract process is running at this moment.
|
||||
*/
|
||||
bool is_contract_running();
|
||||
|
||||
} // namespace proc
|
||||
|
||||
#endif
|
||||
52
src/shared.h
52
src/shared.h
@@ -1,52 +0,0 @@
|
||||
#ifndef _HP_SHARED_H_
|
||||
#define _HP_SHARED_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace shared
|
||||
{
|
||||
|
||||
struct ContractUser
|
||||
{
|
||||
string pubkeyb64;
|
||||
int inpipe[2];
|
||||
int outpipe[2];
|
||||
string outbuffer;
|
||||
|
||||
ContractUser(string _pubkeyb64, int _inpipe[2], int _outpipe[2])
|
||||
{
|
||||
pubkeyb64 = _pubkeyb64;
|
||||
inpipe[0] = _inpipe[0];
|
||||
inpipe[1] = _inpipe[1];
|
||||
outpipe[0] = _outpipe[0];
|
||||
outpipe[1] = _outpipe[1];
|
||||
}
|
||||
};
|
||||
|
||||
struct PeerNode
|
||||
{
|
||||
string pubkeyb64;
|
||||
int inpipe[2];
|
||||
int outpipe[2];
|
||||
|
||||
PeerNode(string _pubkeyb64, int _inpipe[2], int _outpipe[2])
|
||||
{
|
||||
pubkeyb64 = _pubkeyb64;
|
||||
inpipe[0] = _inpipe[0];
|
||||
inpipe[1] = _inpipe[1];
|
||||
outpipe[0] = _outpipe[0];
|
||||
outpipe[1] = _outpipe[1];
|
||||
}
|
||||
};
|
||||
|
||||
int base64_encode(unsigned char *bin, size_t bin_len, string &encoded_string);
|
||||
int base64_decode(string &base64_str, unsigned char *decoded, size_t decoded_len);
|
||||
void replace_string_contents(string &str, const char* bytes, size_t bytes_len);
|
||||
int version_compare(string &v1, string &v2);
|
||||
|
||||
} // namespace usr
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <iostream>
|
||||
#include "socket_client.h"
|
||||
#include "socket_client.hpp"
|
||||
|
||||
using tcp = net::ip::tcp;
|
||||
using error = boost::system::error_code;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/beast.hpp>
|
||||
#include "socket_session.h"
|
||||
#include "socket_session_handler.h"
|
||||
#include "socket_session.hpp"
|
||||
#include "socket_session_handler.hpp"
|
||||
|
||||
namespace beast = boost::beast;
|
||||
namespace net = boost::asio;
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include "socket_server.h"
|
||||
#include "socket_server.hpp"
|
||||
|
||||
namespace net = boost::asio; // namespace asio
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define _SOCK_SERVER_LISTENER_H_
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include "socket_session_handler.h"
|
||||
#include "socket_session_handler.hpp"
|
||||
|
||||
namespace net = boost::asio; // namespace asio
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
#include "socket_session.h"
|
||||
#include "socket_session.hpp"
|
||||
|
||||
namespace net = boost::asio;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <vector>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/beast.hpp>
|
||||
#include "socket_session_handler.h"
|
||||
#include "socket_session_handler.hpp"
|
||||
|
||||
namespace beast = boost::beast;
|
||||
namespace net = boost::asio;
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef _SOCK_SESSION_HANDLER_H_
|
||||
#define _SOCK_SESSION_HANDLER_H_
|
||||
|
||||
#include "socket_session.h"
|
||||
#include "socket_session.hpp"
|
||||
#include <boost/beast/core.hpp>
|
||||
|
||||
using error = boost::system::error_code;
|
||||
151
src/usr/usr.cpp
151
src/usr/usr.cpp
@@ -8,34 +8,67 @@
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/writer.h>
|
||||
#include <sodium.h>
|
||||
#include "../shared.h"
|
||||
#include "../conf.h"
|
||||
#include "../crypto.h"
|
||||
#include "usr.h"
|
||||
#include "../util.hpp"
|
||||
#include "../conf.hpp"
|
||||
#include "../crypto.hpp"
|
||||
#include "usr.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace shared;
|
||||
using namespace util;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace usr
|
||||
{
|
||||
|
||||
map<string, ContractUser> users;
|
||||
/**
|
||||
* Global user list. (Exposed to other sub systems)
|
||||
*/
|
||||
map<string, contract_user> users;
|
||||
|
||||
/**
|
||||
* Json schema doc used for user challenge-response json validation.
|
||||
*/
|
||||
Document challenge_response_schemadoc;
|
||||
|
||||
void create_user_challenge(string &msg, string &challenge)
|
||||
int init()
|
||||
{
|
||||
//We initialize the response schema doc from this json string so we can
|
||||
//use the schema repeatedly for all challenge-response validations.
|
||||
|
||||
const char *challenge_response_schema =
|
||||
"{"
|
||||
"\"type\": \"object\","
|
||||
"\"required\": [ \"type\", \"challenge\", \"sig\", \"pubkey\" ],"
|
||||
"\"properties\": {"
|
||||
"\"type\": { \"type\": \"string\" },"
|
||||
"\"challenge\": { \"type\": \"string\" },"
|
||||
"\"sig\": { \"type\": \"string\" },"
|
||||
"\"pubkey\": { \"type\": \"string\" }"
|
||||
"}"
|
||||
"}";
|
||||
challenge_response_schemadoc.Parse(challenge_response_schema);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
base64_encode(challenge_bytes, USER_CHALLENGE_LEN, challenge);
|
||||
//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);
|
||||
|
||||
//Construct the challenge msg json.
|
||||
Document d;
|
||||
d.SetObject();
|
||||
Document::AllocatorType &allocator = d.GetAllocator();
|
||||
d.AddMember("version", StringRef(_HP_VERSION_), allocator);
|
||||
d.AddMember("type", "public_challenge", allocator);
|
||||
d.AddMember("challenge", StringRef(challenge.data()), allocator);
|
||||
d.AddMember("type", MSG_PUBLIC_CHALLENGE, allocator);
|
||||
d.AddMember("challenge", StringRef(challengeb64.data()), allocator);
|
||||
|
||||
StringBuffer buffer;
|
||||
Writer<StringBuffer> writer(buffer);
|
||||
@@ -43,86 +76,117 @@ void create_user_challenge(string &msg, string &challenge)
|
||||
msg = buffer.GetString();
|
||||
}
|
||||
|
||||
bool verify_user_challenge_response(string &response, string &original_challenge, string &extracted_pubkeyb64)
|
||||
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.
|
||||
Document d;
|
||||
d.Parse(response.data());
|
||||
|
||||
//Validate json scheme.
|
||||
//This has a cost. But we have to do this first. Otherwise field value
|
||||
//extraction will fail in subsequent steps if the message is malformed.
|
||||
SchemaDocument schema(challenge_response_schemadoc);
|
||||
SchemaValidator validator(schema);
|
||||
if (!d.Accept(validator))
|
||||
{
|
||||
cerr << "User challenge resposne schema invalid.\n";
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Validate msg type.
|
||||
string type = d["type"].GetString();
|
||||
if (type != "challenge_response")
|
||||
if (type != MSG_CHALLENGE_RESP)
|
||||
{
|
||||
cerr << "User challenge response type invalid. 'challenge_response' expeced.\n";
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Compare the response challenge string with the original issued challenge.
|
||||
string challenge = d["challenge"].GetString();
|
||||
string sigb64 = d["sig"].GetString();
|
||||
string pubkeyb64 = d["pubkey"].GetString();
|
||||
|
||||
if (challenge != original_challenge)
|
||||
{
|
||||
cerr << "User challenge resposne: challenge mismatch.\n";
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!crypto::verify_b64(original_challenge, sigb64, pubkeyb64))
|
||||
//Verify the challenge signature. We do this last due to signature verification cost.
|
||||
string sigb64 = d["sig"].GetString();
|
||||
extracted_pubkeyb64 = d["pubkey"].GetString();
|
||||
if (crypto::verify_b64(original_challenge, sigb64, extracted_pubkeyb64) != 0)
|
||||
{
|
||||
cerr << "User challenge response signature verification failed.\n";
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extracted_pubkeyb64 = pubkeyb64;
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void add_user(string &pubkeyb64)
|
||||
int add_user(const string &pubkeyb64)
|
||||
{
|
||||
if (users.count(pubkeyb64) == 1)
|
||||
{
|
||||
cerr << pubkeyb64 << " already exist. Cannot add user.\n";
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Establish the I/O pipes for [User <--> SC] channel.
|
||||
|
||||
//inpipe: User will write input to this and contract will read user-input from this.
|
||||
int inpipe[2];
|
||||
int outpipe[2];
|
||||
if (pipe(inpipe) != 0 || pipe(outpipe) != 0)
|
||||
if (pipe(inpipe) != 0)
|
||||
{
|
||||
cerr << "User pipe creation failed. pubkey:" << pubkeyb64 << endl;
|
||||
return;
|
||||
cerr << "User in pipe creation failed. pubkey:" << pubkeyb64 << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
users.insert(pair<string, ContractUser>(pubkeyb64, ContractUser(pubkeyb64, inpipe, outpipe)));
|
||||
//outpipe: Contract will write output for the user to this and user will read from this.
|
||||
int outpipe[2];
|
||||
if (pipe(outpipe) != 0)
|
||||
{
|
||||
cerr << "User out pipe creation failed. pubkey:" << pubkeyb64 << endl;
|
||||
|
||||
//We need to close 'inpipe' in case outpipe failed.
|
||||
close(inpipe[0]);
|
||||
close(inpipe[1]);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
users.insert(pair<string, contract_user>(pubkeyb64, contract_user(pubkeyb64, inpipe, outpipe)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void remove_user(string &pubkeyb64)
|
||||
int remove_user(const string &pubkeyb64)
|
||||
{
|
||||
if (users.count(pubkeyb64) == 0)
|
||||
{
|
||||
cerr << pubkeyb64 << " does not exist. Cannot remove user.\n";
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto itr = users.find(pubkeyb64);
|
||||
ContractUser user = itr->second;
|
||||
contract_user user = itr->second;
|
||||
|
||||
//Close the User <--> SC I/O pipes.
|
||||
close(user.inpipe[0]);
|
||||
close(user.inpipe[1]);
|
||||
close(user.outpipe[0]);
|
||||
close(user.outpipe[1]);
|
||||
|
||||
users.erase(itr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Read per-user outputs produced by the contract process.
|
||||
int read_contract_user_outputs()
|
||||
{
|
||||
//Read any outputs that has been written by the contract process
|
||||
//from all the user outpipes and store in the outbuffer of each user.
|
||||
//User outbuffer will be read by the consensus process later when it wishes so.
|
||||
|
||||
//Future optmization: Read and populate user buffers parallely.
|
||||
//Currently this is sequential for simplicity which will not scale well
|
||||
//when there are large number of users connected to the same HP node.
|
||||
|
||||
for (auto &[pk, user] : users)
|
||||
{
|
||||
int fdout = user.outpipe[0];
|
||||
@@ -135,7 +199,7 @@ int read_contract_user_outputs()
|
||||
read(fdout, data, bytes_available);
|
||||
|
||||
//Populate the user output buffer with new data
|
||||
shared::replace_string_contents(user.outbuffer, data, bytes_available);
|
||||
util::replace_string_contents(user.outbuffer, data, bytes_available);
|
||||
|
||||
cout << "Read " + to_string(bytes_available) << " bytes into user output buffer. user:" + user.pubkeyb64 << endl;
|
||||
}
|
||||
@@ -144,23 +208,4 @@ int read_contract_user_outputs()
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init()
|
||||
{
|
||||
const char *challenge_response_schema =
|
||||
"{"
|
||||
"\"type\": \"object\","
|
||||
"\"required\": [ \"type\", \"challenge\", \"sig\", \"pubkey\" ],"
|
||||
"\"properties\": {"
|
||||
"\"type\": { \"type\": \"string\" },"
|
||||
"\"challenge\": { \"type\": \"string\" },"
|
||||
"\"sig\": { \"type\": \"string\" },"
|
||||
"\"pubkey\": { \"type\": \"string\" }"
|
||||
"}"
|
||||
"}";
|
||||
|
||||
challenge_response_schemadoc.Parse(challenge_response_schema);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace usr
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef _HP_USR_H_
|
||||
#define _HP_USR_H_
|
||||
|
||||
#define USER_CHALLENGE_LEN 16
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "../shared.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace shared;
|
||||
|
||||
namespace usr
|
||||
{
|
||||
extern map<string, ContractUser> users;
|
||||
|
||||
int init();
|
||||
void create_user_challenge(string &msg, string &challenge);
|
||||
bool verify_user_challenge_response(string &response, string &original_challenge, string &extracted_pubkey);
|
||||
void add_user(string &pubkeyb64);
|
||||
void remove_user(string &pubkeyb64);
|
||||
|
||||
} // namespace usr
|
||||
|
||||
#endif
|
||||
97
src/usr/usr.hpp
Normal file
97
src/usr/usr.hpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#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 <cstdio>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "../util.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace util;
|
||||
|
||||
/**
|
||||
* Maintains the global user list with pending input outputs and manages user connections.
|
||||
*/
|
||||
namespace usr
|
||||
{
|
||||
|
||||
/**
|
||||
* Global authenticated (challenge-verified) user list.
|
||||
*/
|
||||
extern map<string, contract_user> 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": "<HP version>",
|
||||
* "type": "public_challenge",
|
||||
* "challenge": "<base64 challenge string>"
|
||||
* }
|
||||
* @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": "<original base64 challenge the user received>",
|
||||
* "sig": "<Base64 signature of the challenge>",
|
||||
* "pubkey": "<Base64 public key of the user>"
|
||||
* }
|
||||
* @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
|
||||
|
||||
#endif
|
||||
@@ -3,17 +3,24 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace shared
|
||||
namespace util
|
||||
{
|
||||
|
||||
void replace_string_contents(string &str, const char *bytes, size_t bytes_len);
|
||||
|
||||
int base64_encode(unsigned char *bin, size_t bin_len, string &encoded_string)
|
||||
void replace_string_contents(string &str, const char *bytes, size_t bytes_len)
|
||||
{
|
||||
if (str.length() > 0)
|
||||
str.clear();
|
||||
str.append(bytes, bytes_len);
|
||||
}
|
||||
|
||||
int base64_encode(const unsigned char *bin, size_t bin_len, string &encoded_string)
|
||||
{
|
||||
// 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];
|
||||
|
||||
char *encoded_str_char = sodium_bin2base64(
|
||||
// Get encoded string.
|
||||
const char *encoded_str_char = sodium_bin2base64(
|
||||
base64chars, base64_len,
|
||||
bin, bin_len,
|
||||
sodium_base64_VARIANT_ORIGINAL);
|
||||
@@ -21,11 +28,12 @@ int base64_encode(unsigned char *bin, size_t bin_len, string &encoded_string)
|
||||
if (encoded_str_char == NULL)
|
||||
return -1;
|
||||
|
||||
// Assign the encoded char* onto the provided string reference.
|
||||
replace_string_contents(encoded_string, base64chars, base64_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int base64_decode(string &base64_str, unsigned char *decoded, size_t decoded_len)
|
||||
int base64_decode(const string &base64_str, unsigned char *decoded, size_t decoded_len)
|
||||
{
|
||||
const char *b64_end;
|
||||
size_t bin_len;
|
||||
@@ -41,17 +49,10 @@ int base64_decode(string &base64_str, unsigned char *decoded, size_t decoded_len
|
||||
return 0;
|
||||
}
|
||||
|
||||
void replace_string_contents(string &str, const char *bytes, size_t bytes_len)
|
||||
{
|
||||
if (str.length() > 0)
|
||||
str.clear();
|
||||
str.append(bytes, bytes_len);
|
||||
}
|
||||
|
||||
// v1 < v2 -> -1
|
||||
// v1 == v2 -> 0
|
||||
// v1 > v2 -> +1
|
||||
int version_compare(string &v1, string &v2)
|
||||
int version_compare(const string &v1, const string &v2)
|
||||
{
|
||||
size_t i = 0, j = 0;
|
||||
while (i < v1.length() || j < v2.length())
|
||||
@@ -80,4 +81,4 @@ int version_compare(string &v1, string &v2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace shared
|
||||
} // namespace util
|
||||
89
src/util.hpp
Normal file
89
src/util.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef _HP_UTIL_H_
|
||||
#define _HP_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace util
|
||||
{
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
contract_user(const string &_pubkeyb64, int _inpipe[2], int _outpipe[2])
|
||||
{
|
||||
pubkeyb64 = _pubkeyb64;
|
||||
inpipe[0] = _inpipe[0];
|
||||
inpipe[1] = _inpipe[1];
|
||||
outpipe[0] = _outpipe[0];
|
||||
outpipe[1] = _outpipe[1];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds information about a HotPocket peer connected to this node.
|
||||
*/
|
||||
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
|
||||
|
||||
peer_node(const string &_pubkeyb64, int _inpipe[2], int _outpipe[2])
|
||||
{
|
||||
pubkeyb64 = _pubkeyb64;
|
||||
inpipe[0] = _inpipe[0];
|
||||
inpipe[1] = _inpipe[1];
|
||||
outpipe[0] = _outpipe[0];
|
||||
outpipe[1] = _outpipe[1];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user