mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
Merged latest from master.
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -4,4 +4,8 @@
|
||||
.*.sw?
|
||||
.DS_Store
|
||||
build/**
|
||||
.vscode/**
|
||||
.vscode/**
|
||||
Makefile
|
||||
CMakeCache.txt
|
||||
cmake_install.cmake
|
||||
CMakeFiles/**
|
||||
21
CMakeLists.txt
Normal file
21
CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
add_definitions("-std=c++17")
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY build)
|
||||
|
||||
add_executable(hpcore
|
||||
src/main.cpp
|
||||
src/conf.cpp
|
||||
src/crypto.cpp
|
||||
src/proc.cpp
|
||||
src/shared.cpp
|
||||
src/usr/usr.cpp
|
||||
src/shared.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(hpcore
|
||||
libsodium.a
|
||||
libboost_system.a
|
||||
libboost_filesystem.a
|
||||
)
|
||||
44
README.md
44
README.md
@@ -13,7 +13,7 @@ A C++ version of hotpocket designed for production envrionments, original protot
|
||||
|
||||
## Steps to setup Hot Pocket
|
||||
|
||||
### Install Libsodium
|
||||
#### Install Libsodium
|
||||
Instructions are based on [this](https://libsodium.gitbook.io/doc/installation).
|
||||
|
||||
1. Download and extract Libsodium 1.0.18 from [here](https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable.tar.gz).
|
||||
@@ -54,10 +54,42 @@ Instructions are based on [this](https://github.com/protocolbuffers/protobuf/tre
|
||||
|
||||
This will update your library cache and avoid potential issues when running your compiled C++ program which links to newly installed libraries.
|
||||
|
||||
#### Install CMAKE
|
||||
If you use apt, run `sudo apt install cmake`
|
||||
Or follow [this](https://cmake.org/install/)
|
||||
|
||||
#### Build and run Hot Pocket
|
||||
1. navigate to hotpocket repo root.
|
||||
2. Run `make`
|
||||
3. Run `./build/hpcore new ~/mycontract`. This will initialize a new contract directory `mycontract` in your home directory.
|
||||
4. Take a look at `~/mycontract/cfg/hp.cfg`. This is your new contract config file. You can modify it according to your contract hosting requirements.
|
||||
5. Optional: Run `./build/hpcore rekey ~/mycontract` to generate new public/private key pair.
|
||||
6. Run `./build/hpcore run ~/mycontract` to run your smart contract (to do).
|
||||
1. Run `cmake .` (You only have to do this once)
|
||||
1. Run `make`
|
||||
1. Run `./build/hpcore new ~/mycontract`. This will initialize a new contract directory `mycontract` in your home directory.
|
||||
1. Take a look at `~/mycontract/cfg/hp.cfg`. This is your new contract config file. You can modify it according to your contract hosting requirements.
|
||||
1. Optional: Run `./build/hpcore rekey ~/mycontract` to generate new public/private key pair.
|
||||
1. Run `./build/hpcore run ~/mycontract` to run your smart contract (to do).
|
||||
|
||||
## Code structure
|
||||
Code is divided into subsystems via namespaces. Some subsystems mentioned here are yet to be introduced.
|
||||
|
||||
#### conf
|
||||
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.
|
||||
|
||||
#### proc
|
||||
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**.
|
||||
|
||||
#### cons
|
||||
Handles consensus and proposal rounds. Makes use of **usr**, **ntn** and **proc**
|
||||
|
||||
#### sock
|
||||
Handles generic web sockets functionality. Mainly acts as a wrapper for boost/beast.
|
||||
|
||||
#### shared
|
||||
Contains shared data structures/helper functions used by multiple subsystems. Used by most of the subsystems.
|
||||
4
makefile
4
makefile
@@ -1,4 +0,0 @@
|
||||
all:
|
||||
mkdir -p build
|
||||
g++ src/*.cpp src/p2p/*.cc -lsodium -lboost_system -lboost_filesystem -pthread -lprotobuf -std=c++17 -o build/hpcore
|
||||
echo 'build successful, binary in '`pwd`'/build/hpcore'
|
||||
302
src/conf.cpp
302
src/conf.cpp
@@ -1,6 +1,7 @@
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sodium.h>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/schema.h>
|
||||
#include <rapidjson/istreamwrapper.h>
|
||||
@@ -8,6 +9,8 @@
|
||||
#include <rapidjson/prettywriter.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "conf.h"
|
||||
#include "crypto.h"
|
||||
#include "shared.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
@@ -18,74 +21,35 @@ namespace conf
|
||||
ContractCtx ctx;
|
||||
ContractConfig cfg;
|
||||
|
||||
const char *cfg_schema =
|
||||
"{"
|
||||
"\"type\": \"object\","
|
||||
"\"required\": [ \"version\", \"pubkeyb64\", \"seckeyb64\", \"binary\", \"binargs\", \"listenip\""
|
||||
", \"peers\", \"unl\", \"peerport\", \"roundtime\", \"pubport\", \"pubmaxsize\", \"pubmaxcpm\" ],"
|
||||
"\"properties\": {"
|
||||
"\"version\": { \"type\": \"string\" },"
|
||||
"\"pubkeyb64\": { \"type\": \"string\" },"
|
||||
"\"seckeyb64\": { \"type\": \"string\" },"
|
||||
"\"binary\": { \"type\": \"string\" },"
|
||||
"\"binargs\": { \"type\": \"string\" },"
|
||||
"\"listenip\": { \"type\": \"string\" },"
|
||||
"\"peers\": {"
|
||||
"\"type\": \"array\","
|
||||
"\"items\": { \"type\": \"string\" }"
|
||||
"},"
|
||||
"\"unl\": {"
|
||||
"\"type\": \"array\","
|
||||
"\"items\": { \"type\": \"string\" }"
|
||||
"},"
|
||||
"\"peerport\": { \"type\": \"integer\" },"
|
||||
"\"roundtime\": { \"type\": \"integer\" },"
|
||||
"\"pubport\": { \"type\": \"integer\" },"
|
||||
"\"pubmaxsize\": { \"type\": \"integer\" },"
|
||||
"\"pubmaxcpm\": { \"type\": \"integer\" }"
|
||||
"}"
|
||||
"}";
|
||||
bool validate_config();
|
||||
int load_config();
|
||||
void save_config();
|
||||
void binpair_to_b64();
|
||||
int b64pair_to_bin();
|
||||
bool validate_contract_dir_paths();
|
||||
bool is_schema_valid(Document &d);
|
||||
|
||||
// v1 < v2 -> -1
|
||||
// v1 == v2 -> 0
|
||||
// v1 > v2 -> +1
|
||||
int version_compare(std::string v1, std::string v2)
|
||||
int init()
|
||||
{
|
||||
size_t i = 0, j = 0;
|
||||
while (i < v1.length() || j < v2.length())
|
||||
{
|
||||
int acc1 = 0, acc2 = 0;
|
||||
if (!validate_contract_dir_paths() || load_config() != 0 || !validate_config())
|
||||
return -1;
|
||||
|
||||
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 (acc1 < acc2)
|
||||
return -1;
|
||||
if (acc1 > acc2)
|
||||
return +1;
|
||||
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_schema_valid(Document &d)
|
||||
int rekey()
|
||||
{
|
||||
Document sd;
|
||||
sd.Parse(cfg_schema);
|
||||
SchemaDocument schema(sd);
|
||||
if (load_config() != 0)
|
||||
return -1;
|
||||
|
||||
SchemaValidator validator(schema);
|
||||
return d.Accept(validator);
|
||||
crypto::generate_signing_keys(cfg.pubkey, cfg.seckey);
|
||||
binpair_to_b64();
|
||||
|
||||
save_config();
|
||||
|
||||
cout << "New signing keys generated at " << ctx.configFile << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_config()
|
||||
@@ -97,22 +61,29 @@ int load_config()
|
||||
if (d.ParseStream(isw).HasParseError())
|
||||
{
|
||||
cerr << "Invalid config file format. Parser error at position " << d.GetErrorOffset() << endl;
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
else if (!is_schema_valid(d))
|
||||
{
|
||||
cerr << "Invalid config file format.\n";
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Check contract version.
|
||||
string cfgVersion = d["version"].GetString();
|
||||
if (version_compare(cfgVersion, _HP_MIN_CONTRACT_VERSION_) == -1)
|
||||
string cfgversion = d["version"].GetString();
|
||||
if (cfgversion.empty())
|
||||
{
|
||||
cerr << "Contract config version missing.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
string minversion = string(_HP_MIN_CONTRACT_VERSION_);
|
||||
if (shared::version_compare(cfgversion, minversion) == -1)
|
||||
{
|
||||
cerr << "Contract version too old. Minimum "
|
||||
<< _HP_MIN_CONTRACT_VERSION_ << " required. "
|
||||
<< cfgVersion << " found.\n";
|
||||
return 0;
|
||||
<< cfgversion << " found.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg.pubkeyb64 = d["pubkeyb64"].GetString();
|
||||
@@ -135,7 +106,10 @@ int load_config()
|
||||
cfg.pubmaxsize = d["pubmaxsize"].GetInt();
|
||||
cfg.pubmaxcpm = d["pubmaxcpm"].GetInt();
|
||||
|
||||
return 1;
|
||||
if (b64pair_to_bin() != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void save_config()
|
||||
@@ -151,22 +125,22 @@ void save_config()
|
||||
d.AddMember("listenip", StringRef(cfg.listenip.data()), allocator);
|
||||
|
||||
Value peers(kArrayType);
|
||||
d.AddMember("peers", peers, allocator);
|
||||
for (int i = 0; i < cfg.peers.size(); i++)
|
||||
for (string &peer : cfg.peers)
|
||||
{
|
||||
Value v;
|
||||
v.SetString(StringRef(cfg.peers[i].data()), allocator);
|
||||
v.SetString(StringRef(peer.data()), allocator);
|
||||
peers.PushBack(v, allocator);
|
||||
}
|
||||
d.AddMember("peers", peers, allocator);
|
||||
|
||||
Value unl(kArrayType);
|
||||
d.AddMember("unl", unl, allocator);
|
||||
for (int i = 0; i < cfg.unl.size(); i++)
|
||||
for (string &node : cfg.unl)
|
||||
{
|
||||
Value v;
|
||||
v.SetString(StringRef(cfg.unl[i].data()), allocator);
|
||||
v.SetString(StringRef(node.data()), allocator);
|
||||
unl.PushBack(v, allocator);
|
||||
}
|
||||
d.AddMember("unl", unl, allocator);
|
||||
|
||||
d.AddMember("peerport", cfg.peerport, allocator);
|
||||
d.AddMember("roundtime", cfg.roundtime, allocator);
|
||||
@@ -181,6 +155,100 @@ void save_config()
|
||||
d.Accept(writer);
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
cerr << "Error decoding base64 public key.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (shared::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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool validate_config()
|
||||
{
|
||||
if (cfg.pubkeyb64.empty() || cfg.seckeyb64.empty())
|
||||
{
|
||||
cerr << "Signing keys missing. Run with 'rekey' to generate new keys.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!boost::filesystem::exists(cfg.binary))
|
||||
{
|
||||
cerr << "Contract binary does not exist: " << cfg.binary << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
//Sign and verify a sample 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))
|
||||
{
|
||||
cerr << "Invalid signing keys. Run with 'rekey' to generate new keys.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_contract_dir_paths(string basedir)
|
||||
{
|
||||
if (basedir[basedir.size() - 1] == '/')
|
||||
@@ -191,62 +259,60 @@ void set_contract_dir_paths(string basedir)
|
||||
ctx.configFile = ctx.configDir + "/hp.cfg";
|
||||
ctx.histDir = basedir + "/hist";
|
||||
ctx.stateDir = basedir + "/state";
|
||||
ctx.binDir = basedir + "/bin";
|
||||
}
|
||||
|
||||
int create_contract()
|
||||
bool validate_contract_dir_paths()
|
||||
{
|
||||
if (boost::filesystem::exists(ctx.contractDir))
|
||||
string dirs[4] = {ctx.contractDir, ctx.configFile, ctx.histDir, ctx.stateDir};
|
||||
|
||||
for (string &dir : dirs)
|
||||
{
|
||||
cerr << "Contract dir already exists.\n";
|
||||
return 0;
|
||||
if (!boost::filesystem::exists(dir))
|
||||
{
|
||||
cerr << dir << " does not exist.\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boost::filesystem::create_directories(ctx.configDir);
|
||||
boost::filesystem::create_directories(ctx.binDir);
|
||||
boost::filesystem::create_directories(ctx.histDir);
|
||||
boost::filesystem::create_directories(ctx.stateDir);
|
||||
|
||||
//Create config file with default settings.
|
||||
cfg.listenip = "0.0.0.0";
|
||||
cfg.peerport = 22860;
|
||||
cfg.roundtime = 1000;
|
||||
cfg.pubport = 8080;
|
||||
cfg.pubmaxsize = 65536;
|
||||
cfg.pubmaxcpm = 100;
|
||||
save_config();
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
int clear_keys()
|
||||
bool is_schema_valid(Document &d)
|
||||
{
|
||||
cfg.pubkeyb64 = "";
|
||||
cfg.seckeyb64 = "";
|
||||
save_config();
|
||||
}
|
||||
const char *cfg_schema =
|
||||
"{"
|
||||
"\"type\": \"object\","
|
||||
"\"required\": [ \"version\", \"pubkeyb64\", \"seckeyb64\", \"binary\", \"binargs\", \"listenip\""
|
||||
", \"peers\", \"unl\", \"peerport\", \"roundtime\", \"pubport\", \"pubmaxsize\", \"pubmaxcpm\" ],"
|
||||
"\"properties\": {"
|
||||
"\"version\": { \"type\": \"string\" },"
|
||||
"\"pubkeyb64\": { \"type\": \"string\" },"
|
||||
"\"seckeyb64\": { \"type\": \"string\" },"
|
||||
"\"binary\": { \"type\": \"string\" },"
|
||||
"\"binargs\": { \"type\": \"string\" },"
|
||||
"\"listenip\": { \"type\": \"string\" },"
|
||||
"\"peers\": {"
|
||||
"\"type\": \"array\","
|
||||
"\"items\": { \"type\": \"string\" }"
|
||||
"},"
|
||||
"\"unl\": {"
|
||||
"\"type\": \"array\","
|
||||
"\"items\": { \"type\": \"string\" }"
|
||||
"},"
|
||||
"\"peerport\": { \"type\": \"integer\" },"
|
||||
"\"roundtime\": { \"type\": \"integer\" },"
|
||||
"\"pubport\": { \"type\": \"integer\" },"
|
||||
"\"pubmaxsize\": { \"type\": \"integer\" },"
|
||||
"\"pubmaxcpm\": { \"type\": \"integer\" }"
|
||||
"}"
|
||||
"}";
|
||||
|
||||
int init(int argc, char **argv)
|
||||
{
|
||||
if (ctx.command == "new")
|
||||
{
|
||||
if (!create_contract())
|
||||
return 0;
|
||||
}
|
||||
Document sd;
|
||||
sd.Parse(cfg_schema);
|
||||
SchemaDocument schema(sd);
|
||||
|
||||
if (!load_config())
|
||||
return 0;
|
||||
|
||||
if (ctx.command == "rekey")
|
||||
{
|
||||
//Clear the keys. crpyto::init will automatically init the keys.
|
||||
clear_keys();
|
||||
}
|
||||
else if (ctx.command == "run")
|
||||
{
|
||||
//TO DO: Contract run logic.
|
||||
}
|
||||
|
||||
return 1;
|
||||
SchemaValidator validator(schema);
|
||||
return d.Accept(validator);
|
||||
}
|
||||
|
||||
} // namespace conf
|
||||
10
src/conf.h
10
src/conf.h
@@ -24,7 +24,6 @@ struct ContractCtx
|
||||
string contractDir;
|
||||
string histDir;
|
||||
string stateDir;
|
||||
string binDir;
|
||||
string configDir;
|
||||
string configFile;
|
||||
};
|
||||
@@ -35,9 +34,9 @@ struct ContractConfig
|
||||
Config elements which are only initialized in memory (these are not loaded from the config file)
|
||||
*/
|
||||
//public key bytes
|
||||
unsigned char *pubkey;
|
||||
string pubkey;
|
||||
//secret key bytes
|
||||
unsigned char *seckey;
|
||||
string seckey;
|
||||
|
||||
/*
|
||||
Config elements which are loaded from the config file.
|
||||
@@ -58,8 +57,11 @@ struct ContractConfig
|
||||
|
||||
extern ContractCtx ctx;
|
||||
extern ContractConfig cfg;
|
||||
int init(int argc, char **argv);
|
||||
int init();
|
||||
int rekey();
|
||||
int create_contract();
|
||||
void set_contract_dir_paths(string basedir);
|
||||
int load_config();
|
||||
void save_config();
|
||||
|
||||
} // namespace conf
|
||||
|
||||
156
src/crypto.cpp
156
src/crypto.cpp
@@ -1,48 +1,54 @@
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <sodium.h>
|
||||
#include "conf.h"
|
||||
#include "crypto.h"
|
||||
#include "shared.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
void generate_crypto_keys();
|
||||
string base64_encode(unsigned char *bin, size_t bin_len);
|
||||
int base64_decode(string base64_str, unsigned char *decoded, size_t decoded_len);
|
||||
void generate_signing_keys();
|
||||
void binpair_to_b64();
|
||||
int b64pair_to_bin();
|
||||
|
||||
void sign(const unsigned char *msg, unsigned long long msg_len, unsigned char *sig)
|
||||
string sign(string &msg, string &seckey)
|
||||
{
|
||||
crypto_sign_detached(sig, NULL, msg, msg_len, conf::cfg.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 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.size() + 1, conf::cfg.seckey);
|
||||
return base64_encode(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(const unsigned char *msg, unsigned long long msg_len, const unsigned char *sig, const unsigned char *pubkey)
|
||||
bool verify(string &msg, string &sig, string &pubkey)
|
||||
{
|
||||
int result = crypto_sign_verify_detached(sig, msg, msg_len, 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)
|
||||
bool verify_b64(string &msg, string &sigb64, string &pubkeyb64)
|
||||
{
|
||||
unsigned char decoded_pubkey[crypto_sign_PUBLICKEYBYTES];
|
||||
base64_decode(pubkeyb64, decoded_pubkey, crypto_sign_PUBLICKEYBYTES);
|
||||
shared::base64_decode(pubkeyb64, decoded_pubkey, crypto_sign_PUBLICKEYBYTES);
|
||||
|
||||
unsigned char decoded_sig[crypto_sign_BYTES];
|
||||
base64_decode(sigb64, 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.size() + 1, decoded_pubkey);
|
||||
int result = crypto_sign_verify_detached(decoded_sig, (unsigned char *)msg.data(), msg.length(), decoded_pubkey);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
@@ -51,122 +57,20 @@ int init()
|
||||
if (sodium_init() < 0)
|
||||
{
|
||||
cerr << "sodium_init failed.\n";
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conf::ctx.command == "new" || conf::ctx.command == "rekey")
|
||||
{
|
||||
cout << "Generating new keys.\n";
|
||||
generate_crypto_keys();
|
||||
binpair_to_b64();
|
||||
|
||||
conf::save_config();
|
||||
}
|
||||
else if (conf::ctx.command == "run")
|
||||
{
|
||||
if (conf::cfg.pubkeyb64.empty() || conf::cfg.seckeyb64.empty())
|
||||
{
|
||||
cerr << "Signing keys missing. Run with 'rekey' to generate new keys.\n";
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Decode b64 keys into bytes and store in memory.
|
||||
if (!b64pair_to_bin())
|
||||
return 0;
|
||||
|
||||
//Sign and verify a sample to ensure we have a matching key pair.
|
||||
string msg = "hotpocket";
|
||||
string sigb64 = sign_b64(msg);
|
||||
if (!verify_b64(msg, sigb64, conf::cfg.pubkeyb64))
|
||||
{
|
||||
cerr << "Invalid signing keys. Run with 'rekey' to generate new keys.\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void generate_crypto_keys()
|
||||
void generate_signing_keys(string &pubkey, string &seckey)
|
||||
{
|
||||
if (conf::cfg.pubkey != NULL)
|
||||
free(conf::cfg.pubkey);
|
||||
unsigned char pubkeychars[crypto_sign_PUBLICKEYBYTES];
|
||||
unsigned char seckeychars[crypto_sign_SECRETKEYBYTES];
|
||||
crypto_sign_keypair(pubkeychars, seckeychars);
|
||||
|
||||
if (conf::cfg.seckey != NULL)
|
||||
free(conf::cfg.seckey);
|
||||
|
||||
conf::cfg.pubkey = (unsigned char *)malloc(crypto_sign_PUBLICKEYBYTES);
|
||||
conf::cfg.seckey = (unsigned char *)malloc(crypto_sign_SECRETKEYBYTES);
|
||||
crypto_sign_keypair(conf::cfg.pubkey, conf::cfg.seckey);
|
||||
}
|
||||
|
||||
string base64_encode(unsigned char *bin, size_t bin_len)
|
||||
{
|
||||
const size_t base64_max_len = sodium_base64_encoded_len(bin_len, sodium_base64_VARIANT_ORIGINAL);
|
||||
char base64_str[base64_max_len];
|
||||
|
||||
char *encoded_str_char = sodium_bin2base64(
|
||||
base64_str, base64_max_len,
|
||||
bin, bin_len,
|
||||
sodium_base64_VARIANT_ORIGINAL);
|
||||
|
||||
if (encoded_str_char == NULL)
|
||||
throw "Base64 Error: Failed to encode string";
|
||||
|
||||
string s(base64_str);
|
||||
return s;
|
||||
}
|
||||
|
||||
int base64_decode(string base64_str, unsigned char *decoded, size_t decoded_len)
|
||||
{
|
||||
const char *b64_end;
|
||||
size_t bin_len;
|
||||
if (sodium_base642bin(
|
||||
decoded, decoded_len,
|
||||
base64_str.data(), base64_str.size() + 1,
|
||||
"", &bin_len, &b64_end,
|
||||
sodium_base64_VARIANT_ORIGINAL))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void binpair_to_b64()
|
||||
{
|
||||
conf::cfg.pubkeyb64 = base64_encode(conf::cfg.pubkey, crypto_sign_PUBLICKEYBYTES);
|
||||
conf::cfg.seckeyb64 = base64_encode(conf::cfg.seckey, crypto_sign_SECRETKEYBYTES);
|
||||
}
|
||||
|
||||
int b64pair_to_bin()
|
||||
{
|
||||
unsigned char *decoded_pubkey = (unsigned char *)malloc(crypto_sign_PUBLICKEYBYTES);
|
||||
unsigned char *decoded_seckey = (unsigned char *)malloc(crypto_sign_SECRETKEYBYTES);
|
||||
|
||||
if (!base64_decode(conf::cfg.pubkeyb64, decoded_pubkey, crypto_sign_PUBLICKEYBYTES))
|
||||
{
|
||||
cerr << "Error decoding public key.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!base64_decode(conf::cfg.seckeyb64, decoded_seckey, crypto_sign_SECRETKEYBYTES))
|
||||
{
|
||||
cerr << "Error decoding secret key.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (conf::cfg.pubkey != NULL)
|
||||
free(conf::cfg.pubkey);
|
||||
|
||||
if (conf::cfg.seckey != NULL)
|
||||
free(conf::cfg.seckey);
|
||||
|
||||
conf::cfg.pubkey = decoded_pubkey;
|
||||
conf::cfg.seckey = decoded_seckey;
|
||||
return 1;
|
||||
shared::replace_string_contents(pubkey, (char *)pubkeychars, crypto_sign_PUBLICKEYBYTES);
|
||||
shared::replace_string_contents(seckey, (char *)seckeychars, crypto_sign_SECRETKEYBYTES);
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
22
src/crypto.h
22
src/crypto.h
@@ -8,25 +8,27 @@ namespace crypto
|
||||
|
||||
int init();
|
||||
|
||||
/**
|
||||
* Generates the signature for the given message using the contract's secret key.
|
||||
*/
|
||||
void sign(const unsigned char *msg, unsigned long long msg_len, unsigned char *sig);
|
||||
void generate_signing_keys(string &pubkey, string &seckey);
|
||||
|
||||
/**
|
||||
* Returns the base64 signature for the given message using the contract's secret key.
|
||||
* Returns the signature bytes for the given message bytes using the provided secret key bytes.
|
||||
*/
|
||||
string sign_b64(string msg);
|
||||
string sign(string &msg, string &seckey);
|
||||
|
||||
/**
|
||||
* Verifies the given signature with the message using the provided public key.
|
||||
* Returns the base64 signature for the given message bytes using the provided base64 secret key.
|
||||
*/
|
||||
bool verify(const unsigned char *msg, unsigned long long msg_len, const unsigned char *sig, const unsigned char *pubkey);
|
||||
string sign_b64(string &msg, string &seckeyb64);
|
||||
|
||||
/**
|
||||
* Verifies the given base64 signature with the message using the provided base64 public key.
|
||||
* Verifies the given signature bytes for the message bytes using the provided public key bytes.
|
||||
*/
|
||||
bool verify_b64(string msg, string sigb64, string pubkeyb64);
|
||||
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
|
||||
|
||||
|
||||
33
src/main.cpp
33
src/main.cpp
@@ -6,6 +6,7 @@
|
||||
#include <iostream>
|
||||
#include "conf.h"
|
||||
#include "crypto.h"
|
||||
#include "usr/usr.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -13,7 +14,7 @@ int parse_cmd(int argc, char **argv);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (!parse_cmd(argc, argv))
|
||||
if (parse_cmd(argc, argv) != 0)
|
||||
return -1;
|
||||
|
||||
if (conf::ctx.command == "version")
|
||||
@@ -22,11 +23,26 @@ int main(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
bool initSuccess = conf::init(argc, argv) && crypto::init();
|
||||
if (!initSuccess)
|
||||
{
|
||||
cerr << "Init error\n";
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,13 +65,14 @@ int parse_cmd(int argc, char **argv)
|
||||
else
|
||||
{
|
||||
conf::set_contract_dir_paths(argv[2]);
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (command == "version")
|
||||
{
|
||||
if (argc == 2)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,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;
|
||||
}
|
||||
146
src/proc.cpp
Normal file
146
src/proc.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/writer.h>
|
||||
#include "proc.h"
|
||||
#include "conf.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace shared;
|
||||
|
||||
namespace proc
|
||||
{
|
||||
|
||||
/**
|
||||
* Keeps the currently executing contract process id (if any)
|
||||
*/
|
||||
int contract_pid;
|
||||
|
||||
int write_to_stdin(ContractExecArgs &args);
|
||||
bool is_contract_running();
|
||||
|
||||
int exec_contract(ContractExecArgs &args)
|
||||
{
|
||||
if (is_contract_running())
|
||||
{
|
||||
cerr << "Contract process still running.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pid = fork();
|
||||
if (pid > 0)
|
||||
{
|
||||
//HotPocket process.
|
||||
|
||||
contract_pid = pid;
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
//Contract process.
|
||||
//Set up the process environment and overlay the contract binary program with execv().
|
||||
|
||||
//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_to_stdin(args);
|
||||
|
||||
char *execv_args[] = {conf::cfg.binary.data(), conf::cfg.binargs.data(), NULL};
|
||||
execv(execv_args[0], execv_args);
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "fork() failed.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the contract input message into the stdin of the contract process.
|
||||
* Input format:
|
||||
* {
|
||||
* "version":"<hp version>",
|
||||
* "pubkey": "<this node's base64 public key>",
|
||||
* "ts": <this node's timestamp (unix milliseconds)>,
|
||||
* "usrfd":{ "pkb64":[fd0, fd1], ... },
|
||||
* "nplfd":{ "pkb64":[fd0, fd1], ... },
|
||||
* "unl":[ "pkb64", ... ]
|
||||
* }
|
||||
*/
|
||||
int write_to_stdin(ContractExecArgs &args)
|
||||
{
|
||||
Document d;
|
||||
d.SetObject();
|
||||
Document::AllocatorType &allocator = d.GetAllocator();
|
||||
|
||||
d.AddMember("version", StringRef(_HP_VERSION_), allocator);
|
||||
d.AddMember("pubkey", StringRef(conf::cfg.pubkeyb64.data()), allocator);
|
||||
d.AddMember("ts", args.timestamp, allocator);
|
||||
|
||||
Value users(kObjectType);
|
||||
for (auto &[pk, user] : args.users)
|
||||
{
|
||||
Value fdlist(kArrayType);
|
||||
fdlist.PushBack(user.inpipe[0], allocator);
|
||||
fdlist.PushBack(user.outpipe[1], allocator);
|
||||
users.AddMember(StringRef(user.pubkeyb64.data()), fdlist, allocator);
|
||||
}
|
||||
d.AddMember("usrfd", users, allocator);
|
||||
|
||||
Value peers(kObjectType);
|
||||
for (auto &[pk, peer] : args.peers)
|
||||
{
|
||||
Value fdlist(kArrayType);
|
||||
fdlist.PushBack(peer.inpipe[0], allocator);
|
||||
fdlist.PushBack(peer.outpipe[1], allocator);
|
||||
peers.AddMember(StringRef(peer.pubkeyb64.data()), fdlist, allocator);
|
||||
}
|
||||
d.AddMember("nplfd", peers, allocator);
|
||||
|
||||
Value unl(kArrayType);
|
||||
for (string &node : conf::cfg.unl)
|
||||
unl.PushBack(StringRef(node.data()), allocator);
|
||||
d.AddMember("unl", unl, allocator);
|
||||
|
||||
StringBuffer buffer;
|
||||
Writer<StringBuffer> writer(buffer);
|
||||
d.Accept(writer);
|
||||
const char *json = buffer.GetString();
|
||||
|
||||
int stdinpipe[2];
|
||||
if (pipe(stdinpipe) != 0)
|
||||
{
|
||||
cerr << "Failed to create pipe to the contract process.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
dup2(stdinpipe[0], STDIN_FILENO);
|
||||
close(stdinpipe[0]);
|
||||
|
||||
write(stdinpipe[1], json, buffer.GetSize());
|
||||
close(stdinpipe[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_contract_running()
|
||||
{
|
||||
if (contract_pid > 0)
|
||||
{
|
||||
int status = 0;
|
||||
if (!waitpid(contract_pid, &status, WNOHANG))
|
||||
return true;
|
||||
contract_pid = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace proc
|
||||
32
src/proc.h
Normal file
32
src/proc.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#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
|
||||
83
src/shared.cpp
Normal file
83
src/shared.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <string>
|
||||
#include <sodium.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace shared
|
||||
{
|
||||
|
||||
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)
|
||||
{
|
||||
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(
|
||||
base64chars, base64_len,
|
||||
bin, bin_len,
|
||||
sodium_base64_VARIANT_ORIGINAL);
|
||||
|
||||
if (encoded_str_char == NULL)
|
||||
return -1;
|
||||
|
||||
replace_string_contents(encoded_string, base64chars, base64_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int base64_decode(string &base64_str, unsigned char *decoded, size_t decoded_len)
|
||||
{
|
||||
const char *b64_end;
|
||||
size_t bin_len;
|
||||
if (sodium_base642bin(
|
||||
decoded, decoded_len,
|
||||
base64_str.data(), base64_str.size() + 1,
|
||||
"", &bin_len, &b64_end,
|
||||
sodium_base64_VARIANT_ORIGINAL))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
size_t i = 0, j = 0;
|
||||
while (i < v1.length() || j < v2.length())
|
||||
{
|
||||
int acc1 = 0, acc2 = 0;
|
||||
|
||||
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 (acc1 < acc2)
|
||||
return -1;
|
||||
if (acc1 > acc2)
|
||||
return +1;
|
||||
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace shared
|
||||
52
src/shared.h
Normal file
52
src/shared.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#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
|
||||
166
src/usr/usr.cpp
Normal file
166
src/usr/usr.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/schema.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/writer.h>
|
||||
#include <sodium.h>
|
||||
#include "../shared.h"
|
||||
#include "../conf.h"
|
||||
#include "../crypto.h"
|
||||
#include "usr.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace shared;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace usr
|
||||
{
|
||||
|
||||
map<string, ContractUser> users;
|
||||
Document challenge_response_schemadoc;
|
||||
|
||||
void create_user_challenge(string &msg, string &challenge)
|
||||
{
|
||||
unsigned char challenge_bytes[USER_CHALLENGE_LEN];
|
||||
randombytes_buf(challenge_bytes, USER_CHALLENGE_LEN);
|
||||
|
||||
base64_encode(challenge_bytes, USER_CHALLENGE_LEN, challenge);
|
||||
|
||||
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);
|
||||
|
||||
StringBuffer buffer;
|
||||
Writer<StringBuffer> writer(buffer);
|
||||
d.Accept(writer);
|
||||
msg = buffer.GetString();
|
||||
}
|
||||
|
||||
bool verify_user_challenge_response(string &response, string &original_challenge, string &extracted_pubkeyb64)
|
||||
{
|
||||
Document d;
|
||||
d.Parse(response.data());
|
||||
|
||||
SchemaDocument schema(challenge_response_schemadoc);
|
||||
SchemaValidator validator(schema);
|
||||
if (!d.Accept(validator))
|
||||
{
|
||||
cerr << "User challenge resposne schema invalid.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
string type = d["type"].GetString();
|
||||
if (type != "challenge_response")
|
||||
{
|
||||
cerr << "User challenge response type invalid. 'challenge_response' expeced.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!crypto::verify_b64(original_challenge, sigb64, pubkeyb64))
|
||||
{
|
||||
cerr << "User challenge response signature verification failed.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
extracted_pubkeyb64 = pubkeyb64;
|
||||
return true;
|
||||
}
|
||||
|
||||
void add_user(string &pubkeyb64)
|
||||
{
|
||||
if (users.count(pubkeyb64) == 1)
|
||||
{
|
||||
cerr << pubkeyb64 << " already exist. Cannot add user.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
int inpipe[2];
|
||||
int outpipe[2];
|
||||
if (pipe(inpipe) != 0 || pipe(outpipe) != 0)
|
||||
{
|
||||
cerr << "User pipe creation failed. pubkey:" << pubkeyb64 << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
users.insert(pair<string, ContractUser>(pubkeyb64, ContractUser(pubkeyb64, inpipe, outpipe)));
|
||||
}
|
||||
|
||||
void remove_user(string &pubkeyb64)
|
||||
{
|
||||
if (users.count(pubkeyb64) == 0)
|
||||
{
|
||||
cerr << pubkeyb64 << " does not exist. Cannot remove user.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
auto itr = users.find(pubkeyb64);
|
||||
ContractUser user = itr->second;
|
||||
close(user.inpipe[0]);
|
||||
close(user.inpipe[1]);
|
||||
close(user.outpipe[0]);
|
||||
close(user.outpipe[1]);
|
||||
|
||||
users.erase(itr);
|
||||
}
|
||||
|
||||
//Read per-user outputs produced by the contract process.
|
||||
int read_contract_user_outputs()
|
||||
{
|
||||
for (auto &[pk, user] : users)
|
||||
{
|
||||
int fdout = user.outpipe[0];
|
||||
int bytes_available = 0;
|
||||
ioctl(fdout, FIONREAD, &bytes_available);
|
||||
|
||||
if (bytes_available > 0)
|
||||
{
|
||||
char data[bytes_available];
|
||||
read(fdout, data, bytes_available);
|
||||
|
||||
//Populate the user output buffer with new data
|
||||
shared::replace_string_contents(user.outbuffer, data, bytes_available);
|
||||
|
||||
cout << "Read " + to_string(bytes_available) << " bytes into user output buffer. user:" + user.pubkeyb64 << endl;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
26
src/usr/usr.h
Normal file
26
src/usr/usr.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user