mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
User protocol upgrade and js client lib. (#191)
* Unified js client lib for browser and nodejs. * Client lib multiple connections support. * Implemented server challenge response. * Contract guid and version validation. * Server key validation. * User json message encoding improvements.
This commit is contained in:
44
src/conf.cpp
44
src/conf.cpp
@@ -96,13 +96,9 @@ namespace conf
|
||||
crypto::generate_signing_keys(cfg.pubkey, cfg.seckey);
|
||||
binpair_to_hex(cfg);
|
||||
|
||||
// Generate contract id hex.
|
||||
std::string rand_string;
|
||||
crypto::random_bytes(rand_string, 16);
|
||||
util::bin2hex(
|
||||
cfg.contractid,
|
||||
reinterpret_cast<const unsigned char *>(rand_string.data()),
|
||||
rand_string.length());
|
||||
cfg.hpversion = util::HP_VERSION;
|
||||
cfg.contractversion = "1.0";
|
||||
cfg.contractid = crypto::generate_uuid();
|
||||
|
||||
//Add self pubkey to the unl.
|
||||
cfg.unl.emplace(cfg.pubkey);
|
||||
@@ -221,21 +217,21 @@ namespace conf
|
||||
}
|
||||
ifs.close();
|
||||
|
||||
// Check whether the contract version is specified.
|
||||
std::string_view cfgversion = d["version"].as<std::string_view>();
|
||||
if (cfgversion.empty())
|
||||
// Check whether the hp version is specified.
|
||||
cfg.hpversion = d["hpversion"].as<std::string>();
|
||||
if (cfg.hpversion.empty())
|
||||
{
|
||||
std::cerr << "Contract config version missing.\n";
|
||||
std::cerr << "Contract config HP version missing.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check whether this contract complies with the min version requirement.
|
||||
int verresult = util::version_compare(std::string(cfgversion), std::string(util::MIN_CONTRACT_VERSION));
|
||||
// Check whether this config complies with the min version requirement.
|
||||
int verresult = util::version_compare(cfg.hpversion, std::string(util::MIN_CONFIG_VERSION));
|
||||
if (verresult == -1)
|
||||
{
|
||||
std::cerr << "Contract version too old. Minimum "
|
||||
<< util::MIN_CONTRACT_VERSION << " required. "
|
||||
<< cfgversion << " found.\n";
|
||||
std::cerr << "Config version too old. Minimum "
|
||||
<< util::MIN_CONFIG_VERSION << " required. "
|
||||
<< cfg.hpversion << " found.\n";
|
||||
return -1;
|
||||
}
|
||||
else if (verresult == -2)
|
||||
@@ -244,9 +240,18 @@ namespace conf
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Load up the values into the struct.
|
||||
|
||||
cfg.contractid = d["contractid"].as<std::string>();
|
||||
cfg.contractversion = d["contractversion"].as<std::string>();
|
||||
if (cfg.contractid.empty())
|
||||
{
|
||||
std::cerr << "Contract id not specified.\n";
|
||||
return -1;
|
||||
}
|
||||
else if (cfg.contractversion.empty())
|
||||
{
|
||||
std::cerr << "Contract version not specified.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (d["mode"] == MODE_OBSERVER)
|
||||
cfg.operating_mode = OPERATING_MODE::OBSERVER;
|
||||
@@ -372,8 +377,9 @@ namespace conf
|
||||
// Popualte json document with 'cfg' values.
|
||||
// ojson is used instead of json to preserve insertion order.
|
||||
jsoncons::ojson d;
|
||||
d.insert_or_assign("version", util::HP_VERSION);
|
||||
d.insert_or_assign("hpversion", cfg.hpversion);
|
||||
d.insert_or_assign("contractid", cfg.contractid);
|
||||
d.insert_or_assign("contractversion", cfg.contractversion);
|
||||
d.insert_or_assign("mode", cfg.operating_mode == OPERATING_MODE::OBSERVER ? MODE_OBSERVER : MODE_PROPOSER);
|
||||
|
||||
d.insert_or_assign("pubkeyhex", cfg.pubkeyhex);
|
||||
|
||||
@@ -84,7 +84,9 @@ namespace conf
|
||||
bool is_unl = false; // Indicate whether we are a unl node or not.
|
||||
|
||||
// Config elements which are loaded from the config file.
|
||||
std::string hpversion; // Version of Hot Pocket that generated the config.
|
||||
std::string contractid; // Contract guid.
|
||||
std::string contractversion; // Contract version string.
|
||||
OPERATING_MODE operating_mode = OPERATING_MODE::OBSERVER; // Configured startup operating mode of the contract (Observer/Proposer).
|
||||
std::string pubkeyhex; // Contract hex public key
|
||||
std::string seckeyhex; // Contract hex secret key
|
||||
|
||||
@@ -227,4 +227,23 @@ namespace crypto
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::string generate_uuid()
|
||||
{
|
||||
std::string rand_bytes;
|
||||
random_bytes(rand_bytes, 16);
|
||||
|
||||
// Set bits for UUID v4 variant 1.
|
||||
uint8_t *uuid = (uint8_t *)rand_bytes.data();
|
||||
uuid[6] = (uuid[8] & 0x0F) | 0x40;
|
||||
uuid[8] = (uuid[8] & 0xBF) | 0x80;
|
||||
|
||||
std::string hex;
|
||||
util::bin2hex(
|
||||
hex,
|
||||
reinterpret_cast<const unsigned char *>(rand_bytes.data()),
|
||||
rand_bytes.length());
|
||||
|
||||
return hex.substr(0, 8) + "-" + hex.substr(8, 4) + "-" + hex.substr(12, 4) + "-" + hex.substr(16, 4) + "-" + hex.substr(20);
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
@@ -39,6 +39,8 @@ namespace crypto
|
||||
|
||||
std::string get_hash(const std::vector<std::string_view> &sw_vect);
|
||||
|
||||
std::string generate_uuid();
|
||||
|
||||
} // namespace crypto
|
||||
|
||||
#endif
|
||||
@@ -195,9 +195,11 @@ int main(int argc, char **argv)
|
||||
|
||||
hplog::init();
|
||||
|
||||
LOG_INFO << "Hot Pocket " << util::HP_VERSION;
|
||||
LOG_INFO << "Operating mode: "
|
||||
<< (conf::cfg.operating_mode == conf::OPERATING_MODE::OBSERVER ? "Observer" : "Proposer");
|
||||
LOG_INFO << "Public key: " << conf::cfg.pubkeyhex.substr(2); // Public key without 'ed' prefix.
|
||||
LOG_INFO << "Contract: " << conf::cfg.contractid << " (" << conf::cfg.contractversion << ")";
|
||||
|
||||
if (ledger::init() == -1 ||
|
||||
unl::init() == -1 ||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
namespace msg::usrmsg::bson
|
||||
{
|
||||
|
||||
void create_user_challenge(std::vector<uint8_t> &msg, std::string &challengehex);
|
||||
|
||||
void create_status_response(std::vector<uint8_t> &msg, const uint64_t lcl_seq_no, std::string_view lcl);
|
||||
|
||||
void create_contract_input_status(std::vector<uint8_t> &msg, std::string_view status, std::string_view reason,
|
||||
|
||||
@@ -29,41 +29,84 @@ namespace msg::usrmsg::json
|
||||
* @param msg String reference to copy the generated json message string into.
|
||||
* Message format:
|
||||
* {
|
||||
* "type": "handshake_challenge",
|
||||
* "hp_version": "<hp protocol version>",
|
||||
* "type": "user_challenge",
|
||||
* "contract_id": "<contract id>",
|
||||
* "challenge": "<hex challenge string>"
|
||||
* "contract_version": "<contract version string>",
|
||||
* "challenge": "<challenge string>"
|
||||
* }
|
||||
* @param challengehex String reference to copy the generated hex challenge string into.
|
||||
* @param challenge_bytes String reference to copy the generated challenge bytes into.
|
||||
*/
|
||||
void create_user_challenge(std::vector<uint8_t> &msg, std::string &challengehex)
|
||||
void create_user_challenge(std::vector<uint8_t> &msg, std::string &challenge)
|
||||
{
|
||||
// Use libsodium to generate the random challenge bytes.
|
||||
unsigned char challenge_bytes[msg::usrmsg::CHALLENGE_LEN];
|
||||
randombytes_buf(challenge_bytes, msg::usrmsg::CHALLENGE_LEN);
|
||||
|
||||
// We pass the hex challenge string separately to the caller even though
|
||||
// we also include it in the challenge msg as well.
|
||||
|
||||
util::bin2hex(challengehex, challenge_bytes, msg::usrmsg::CHALLENGE_LEN);
|
||||
std::string challenge_bytes;
|
||||
crypto::random_bytes(challenge_bytes, msg::usrmsg::CHALLENGE_LEN);
|
||||
util::bin2hex(challenge,
|
||||
reinterpret_cast<const unsigned char *>(challenge_bytes.data()),
|
||||
msg::usrmsg::CHALLENGE_LEN);
|
||||
|
||||
// Construct the challenge msg json.
|
||||
// We do not use jasoncons library here in favour of performance because this is a simple json message.
|
||||
// We do not use jsoncons library here in favour of performance because this is a simple json message.
|
||||
|
||||
// Since we know the rough size of the challenge message we reserve adequate amount for the holder.
|
||||
// Only Hot Pocket version number is variable length.
|
||||
msg.reserve(256);
|
||||
msg += "{\"";
|
||||
msg += msg::usrmsg::FLD_HP_VERSION;
|
||||
msg += SEP_COLON;
|
||||
msg += msg::usrmsg::USER_PROTOCOL_VERSION;
|
||||
msg += SEP_COMMA;
|
||||
msg += msg::usrmsg::FLD_TYPE;
|
||||
msg += SEP_COLON;
|
||||
msg += msg::usrmsg::MSGTYPE_HANDSHAKE_CHALLENGE;
|
||||
msg += msg::usrmsg::MSGTYPE_USER_CHALLENGE;
|
||||
msg += SEP_COMMA;
|
||||
msg += msg::usrmsg::FLD_CONTRACT_ID;
|
||||
msg += SEP_COLON;
|
||||
msg += conf::cfg.contractid;
|
||||
msg += SEP_COMMA;
|
||||
msg += msg::usrmsg::FLD_CONTRACT_VERSION;
|
||||
msg += SEP_COLON;
|
||||
msg += conf::cfg.contractversion;
|
||||
msg += SEP_COMMA;
|
||||
msg += msg::usrmsg::FLD_CHALLENGE;
|
||||
msg += SEP_COLON;
|
||||
msg += challengehex;
|
||||
msg += challenge;
|
||||
msg += "\"}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs server challenge response message json. This gets sent when we receive
|
||||
* a challenge from the user.
|
||||
*
|
||||
* @param msg String reference to copy the generated json message string into.
|
||||
* Message format:
|
||||
* {
|
||||
* "type": "server_challenge_response",
|
||||
* "sig": "<hex encoded signature of the [challenge + contract_id]>",
|
||||
* "pubkey": "<our public key in hex>"
|
||||
* }
|
||||
* @param original_challenge Original challenge issued by the user.
|
||||
*/
|
||||
void create_server_challenge_response(std::vector<uint8_t> &msg, const std::string &original_challenge)
|
||||
{
|
||||
// Generate signature of challenge + contract id + contract version.
|
||||
const std::string content = original_challenge + conf::cfg.contractid + conf::cfg.contractversion;
|
||||
const std::string sig_hex = crypto::sign_hex(content, conf::cfg.seckeyhex);
|
||||
|
||||
// Since we know the rough size of the challenge message we reserve adequate amount for the holder.
|
||||
msg.reserve(256);
|
||||
msg += "{\"";
|
||||
msg += msg::usrmsg::FLD_TYPE;
|
||||
msg += SEP_COLON;
|
||||
msg += msg::usrmsg::MSGTYPE_SERVER_CHALLENGE_RESPONSE;
|
||||
msg += SEP_COMMA;
|
||||
msg += msg::usrmsg::FLD_SIG;
|
||||
msg += SEP_COLON;
|
||||
msg += sig_hex;
|
||||
msg += SEP_COMMA;
|
||||
msg += msg::usrmsg::FLD_PUBKEY;
|
||||
msg += SEP_COLON;
|
||||
msg += conf::cfg.pubkeyhex;
|
||||
msg += "\"}";
|
||||
}
|
||||
|
||||
@@ -146,16 +189,33 @@ namespace msg::usrmsg::json
|
||||
*/
|
||||
void create_contract_read_response_container(std::vector<uint8_t> &msg, std::string_view content)
|
||||
{
|
||||
msg.reserve(256);
|
||||
msg.reserve(content.size() + 256);
|
||||
msg += "{\"";
|
||||
msg += msg::usrmsg::FLD_TYPE;
|
||||
msg += SEP_COLON;
|
||||
msg += msg::usrmsg::MSGTYPE_CONTRACT_READ_RESPONSE;
|
||||
msg += SEP_COMMA;
|
||||
msg += msg::usrmsg::FLD_CONTENT;
|
||||
msg += SEP_COLON;
|
||||
msg += content;
|
||||
msg += "\"}";
|
||||
msg += SEP_COLON_NOQUOTE;
|
||||
|
||||
if (is_json_string(content))
|
||||
{
|
||||
// Process the final string using jsoncons.
|
||||
jsoncons::json jstring = content;
|
||||
jsoncons::json_options options;
|
||||
options.escape_all_non_ascii(true);
|
||||
|
||||
std::string escaped_content;
|
||||
jstring.dump(escaped_content);
|
||||
|
||||
msg += escaped_content;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg += content;
|
||||
}
|
||||
|
||||
msg += "}";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +232,9 @@ namespace msg::usrmsg::json
|
||||
*/
|
||||
void create_contract_output_container(std::vector<uint8_t> &msg, std::string_view content, const uint64_t lcl_seq_no, std::string_view lcl)
|
||||
{
|
||||
msg.reserve(256);
|
||||
const bool is_string = is_json_string(content);
|
||||
|
||||
msg.reserve(content.size() + 256);
|
||||
msg += "{\"";
|
||||
msg += msg::usrmsg::FLD_TYPE;
|
||||
msg += SEP_COLON;
|
||||
@@ -187,9 +249,26 @@ namespace msg::usrmsg::json
|
||||
msg += std::to_string(lcl_seq_no);
|
||||
msg += SEP_COMMA_NOQUOTE;
|
||||
msg += msg::usrmsg::FLD_CONTENT;
|
||||
msg += SEP_COLON;
|
||||
msg += content;
|
||||
msg += "\"}";
|
||||
msg += SEP_COLON_NOQUOTE;
|
||||
|
||||
if (is_json_string(content))
|
||||
{
|
||||
// Process the final string using jsoncons.
|
||||
jsoncons::json jstring = content;
|
||||
jsoncons::json_options options;
|
||||
options.escape_all_non_ascii(true);
|
||||
|
||||
std::string escaped_content;
|
||||
jstring.dump(escaped_content);
|
||||
|
||||
msg += escaped_content;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg += content;
|
||||
}
|
||||
|
||||
msg += "}";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,80 +277,107 @@ namespace msg::usrmsg::json
|
||||
*
|
||||
* @param extracted_pubkeyhex The hex public key extracted from the response.
|
||||
* @param extracted_protocol The protocol code extracted from the response.
|
||||
* @param extracted_server_challenge Any server challenge issued by user.
|
||||
* @param response The response bytes to verify. This will be parsed as json.
|
||||
* Accepted response format:
|
||||
* {
|
||||
* "type": "handshake_response",
|
||||
* "challenge": "<original hex challenge the user received>",
|
||||
* "type": "user_challenge_response",
|
||||
* "sig": "<hex signature of the challenge>",
|
||||
* "pubkey": "<hex public key of the user>",
|
||||
* "server_challenge": "<hex encoded challenge issued to server>", (max 16 bytes/32 chars)
|
||||
* "protocol": "<json | bson>"
|
||||
* }
|
||||
* @param original_challenge The original hex challenge string issued to the user.
|
||||
* @param original_challenge The original challenge string we issued to the user.
|
||||
* @return 0 if challenge response is verified. -1 if challenge not met or an error occurs.
|
||||
*/
|
||||
int verify_user_handshake_response(std::string &extracted_pubkeyhex, std::string &extracted_protocol,
|
||||
std::string_view response, std::string_view original_challenge)
|
||||
int verify_user_challenge(std::string &extracted_pubkeyhex, std::string &extracted_protocol, std::string &extracted_server_challenge,
|
||||
std::string_view response, std::string_view original_challenge)
|
||||
{
|
||||
jsoncons::json d;
|
||||
if (parse_user_message(d, response) != 0)
|
||||
return -1;
|
||||
|
||||
// Validate msg type.
|
||||
if (d[msg::usrmsg::FLD_TYPE] != msg::usrmsg::MSGTYPE_HANDSHAKE_RESPONSE)
|
||||
if (d[msg::usrmsg::FLD_TYPE] != msg::usrmsg::MSGTYPE_USER_CHALLENGE_RESPONSE)
|
||||
{
|
||||
LOG_DEBUG << "User handshake response type invalid. 'handshake_response' expected.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Compare the response handshake string with the original issued challenge.
|
||||
if (!d.contains(msg::usrmsg::FLD_CHALLENGE) || d[msg::usrmsg::FLD_CHALLENGE] != original_challenge.data())
|
||||
{
|
||||
LOG_DEBUG << "User handshake response 'challenge' invalid.";
|
||||
LOG_DEBUG << "User challenge response type invalid. 'handshake_response' expected.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check for the 'sig' field existence.
|
||||
if (!d.contains(msg::usrmsg::FLD_SIG) || !d[msg::usrmsg::FLD_SIG].is<std::string>())
|
||||
{
|
||||
LOG_DEBUG << "User handshake response 'challenge signature' invalid.";
|
||||
LOG_DEBUG << "User challenge response 'challenge signature' invalid.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check for the 'pubkey' field existence.
|
||||
if (!d.contains(msg::usrmsg::FLD_PUBKEY) || !d[msg::usrmsg::FLD_PUBKEY].is<std::string>())
|
||||
{
|
||||
LOG_DEBUG << "User handshake response 'public key' invalid.";
|
||||
LOG_DEBUG << "User challenge response 'public key' invalid.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check for optional server challenge field existence and valid value.
|
||||
if (d.contains(msg::usrmsg::FLD_SERVER_CHALLENGE))
|
||||
{
|
||||
bool server_challenge_valid = false;
|
||||
|
||||
if (d[msg::usrmsg::FLD_SERVER_CHALLENGE].is<std::string>())
|
||||
{
|
||||
std::string_view challenge = d[msg::usrmsg::FLD_SERVER_CHALLENGE].as<std::string_view>();
|
||||
|
||||
if (!challenge.empty() && challenge.size() <= 32)
|
||||
{
|
||||
server_challenge_valid = true;
|
||||
extracted_server_challenge = challenge;
|
||||
}
|
||||
}
|
||||
|
||||
if (!server_challenge_valid)
|
||||
{
|
||||
LOG_DEBUG << "User challenge response 'server_challenge' invalid.";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for protocol field existence and valid value.
|
||||
if (!d.contains(msg::usrmsg::FLD_PROTOCOL) || !d[msg::usrmsg::FLD_PROTOCOL].is<std::string>())
|
||||
{
|
||||
|
||||
LOG_DEBUG << "User handshake response 'protocol' invalid.";
|
||||
LOG_DEBUG << "User challenge response 'protocol' invalid.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string_view protocolsv = d[msg::usrmsg::FLD_PROTOCOL].as<std::string_view>();
|
||||
if (protocolsv != "json" && protocolsv != "bson")
|
||||
{
|
||||
LOG_DEBUG << "User handshake response 'protocol' type invalid.";
|
||||
LOG_DEBUG << "User challenge response 'protocol' type invalid.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Verify the challenge signature. We do this last due to signature verification cost.
|
||||
std::string_view pubkeysv = d[msg::usrmsg::FLD_PUBKEY].as<std::string_view>();
|
||||
if (crypto::verify_hex(
|
||||
original_challenge,
|
||||
d[msg::usrmsg::FLD_SIG].as<std::string_view>(),
|
||||
pubkeysv) != 0)
|
||||
|
||||
std::string_view pubkey_hex = d[msg::usrmsg::FLD_PUBKEY].as<std::string_view>();
|
||||
std::string pubkey_bytes;
|
||||
pubkey_bytes.resize(crypto::PFXD_PUBKEY_BYTES);
|
||||
util::hex2bin(reinterpret_cast<unsigned char *>(pubkey_bytes.data()),
|
||||
pubkey_bytes.size(),
|
||||
pubkey_hex);
|
||||
|
||||
std::string_view sig_hex = d[msg::usrmsg::FLD_SIG].as<std::string_view>();
|
||||
std::string sig_bytes;
|
||||
sig_bytes.resize(sig_hex.size() / 2);
|
||||
util::hex2bin(reinterpret_cast<unsigned char *>(sig_bytes.data()),
|
||||
sig_bytes.size(),
|
||||
sig_hex);
|
||||
|
||||
if (crypto::verify(original_challenge, sig_bytes, pubkey_bytes) != 0)
|
||||
{
|
||||
LOG_DEBUG << "User challenge response signature verification failed.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
extracted_pubkeyhex = pubkeysv;
|
||||
extracted_pubkeyhex = pubkey_hex;
|
||||
extracted_protocol = protocolsv;
|
||||
|
||||
return 0;
|
||||
@@ -436,4 +542,31 @@ namespace msg::usrmsg::json
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_json_string(std::string_view content)
|
||||
{
|
||||
if (content.empty())
|
||||
return true;
|
||||
|
||||
const char first = content[0];
|
||||
const char last = content[content.size() - 1];
|
||||
|
||||
if ((first == '\"' && last == '\"') ||
|
||||
(first == '{' && last == '}') ||
|
||||
(first == '[' && last == ']') ||
|
||||
content == "true" || content == "false")
|
||||
return false;
|
||||
|
||||
// Check whether all characters are digits.
|
||||
bool decimal_found = false;
|
||||
for (const char c : content)
|
||||
{
|
||||
if ((c != '.' && (c < '0' || c > '9')) || (c == '.' && decimal_found)) // Not a number.
|
||||
return true;
|
||||
else if (c == '.') // There can only be one decimal in a proper number.
|
||||
decimal_found = true;
|
||||
}
|
||||
|
||||
return false; // Is a number.
|
||||
}
|
||||
|
||||
} // namespace msg::usrmsg::json
|
||||
@@ -6,7 +6,9 @@
|
||||
namespace msg::usrmsg::json
|
||||
{
|
||||
|
||||
void create_user_challenge(std::vector<uint8_t> &msg, std::string &challengehex);
|
||||
void create_user_challenge(std::vector<uint8_t> &msg, std::string &challenge);
|
||||
|
||||
void create_server_challenge_response(std::vector<uint8_t> &msg, const std::string &original_challenge);
|
||||
|
||||
void create_status_response(std::vector<uint8_t> &msg, const uint64_t lcl_seq_no, std::string_view lcl);
|
||||
|
||||
@@ -17,8 +19,8 @@ namespace msg::usrmsg::json
|
||||
|
||||
void create_contract_output_container(std::vector<uint8_t> &msg, std::string_view content, const uint64_t lcl_seq_no, std::string_view lcl);
|
||||
|
||||
int verify_user_handshake_response(std::string &extracted_pubkeyhex, std::string &extracted_protocol,
|
||||
std::string_view response, std::string_view original_challenge);
|
||||
int verify_user_challenge(std::string &extracted_pubkeyhex, std::string &extracted_protocol, std::string &extracted_server_challenge,
|
||||
std::string_view response, std::string_view original_challenge);
|
||||
|
||||
int parse_user_message(jsoncons::json &d, std::string_view message);
|
||||
|
||||
@@ -32,6 +34,8 @@ namespace msg::usrmsg::json
|
||||
int extract_input_container(std::string &input, std::string &nonce,
|
||||
uint64_t &max_lcl_seqno, std::string_view contentjson);
|
||||
|
||||
bool is_json_string(std::string_view content);
|
||||
|
||||
} // namespace msg::usrmsg::json
|
||||
|
||||
#endif
|
||||
@@ -7,10 +7,14 @@ namespace msg::usrmsg
|
||||
{
|
||||
// Length of user random challenge bytes.
|
||||
constexpr size_t CHALLENGE_LEN = 16;
|
||||
constexpr const char *USER_PROTOCOL_VERSION = "0.0";
|
||||
|
||||
// Message field names
|
||||
constexpr const char *FLD_HP_VERSION = "hp_version";
|
||||
constexpr const char *FLD_TYPE = "type";
|
||||
constexpr const char *FLD_SERVER_CHALLENGE = "server_challenge";
|
||||
constexpr const char *FLD_CONTRACT_ID = "contract_id";
|
||||
constexpr const char *FLD_CONTRACT_VERSION = "contract_version";
|
||||
constexpr const char *FLD_CHALLENGE = "challenge";
|
||||
constexpr const char *FLD_SIG = "sig";
|
||||
constexpr const char *FLD_PUBKEY = "pubkey";
|
||||
@@ -27,8 +31,9 @@ namespace msg::usrmsg
|
||||
constexpr const char *FLD_REASON = "reason";
|
||||
|
||||
// Message types
|
||||
constexpr const char *MSGTYPE_HANDSHAKE_CHALLENGE = "handshake_challenge";
|
||||
constexpr const char *MSGTYPE_HANDSHAKE_RESPONSE = "handshake_response";
|
||||
constexpr const char *MSGTYPE_USER_CHALLENGE = "user_challenge";
|
||||
constexpr const char *MSGTYPE_USER_CHALLENGE_RESPONSE = "user_challenge_response";
|
||||
constexpr const char *MSGTYPE_SERVER_CHALLENGE_RESPONSE = "server_challenge_response";
|
||||
constexpr const char *MSGTYPE_CONTRACT_READ_REQUEST = "contract_read_request";
|
||||
constexpr const char *MSGTYPE_CONTRACT_READ_RESPONSE = "contract_read_response";
|
||||
constexpr const char *MSGTYPE_CONTRACT_INPUT = "contract_input";
|
||||
|
||||
@@ -92,10 +92,17 @@ namespace usr
|
||||
|
||||
std::string user_pubkey_hex;
|
||||
std::string protocol_code;
|
||||
std::string_view original_challenge = session.issued_challenge;
|
||||
|
||||
if (msg::usrmsg::json::verify_user_handshake_response(user_pubkey_hex, protocol_code, message, original_challenge) == 0)
|
||||
std::string server_challenge;
|
||||
if (msg::usrmsg::json::verify_user_challenge(user_pubkey_hex, protocol_code, server_challenge, message, session.issued_challenge) == 0)
|
||||
{
|
||||
// If user has specified server challange, we need to send a challenge response.
|
||||
if (!server_challenge.empty())
|
||||
{
|
||||
std::vector<uint8_t> msg;
|
||||
msg::usrmsg::json::create_server_challenge_response(msg, server_challenge);
|
||||
session.send(msg);
|
||||
}
|
||||
|
||||
// Challenge signature verification successful. Add the user to our global user list.
|
||||
add_user(session, user_pubkey_hex, protocol_code);
|
||||
return 0;
|
||||
|
||||
@@ -15,8 +15,8 @@ namespace util
|
||||
// Hot Pocket version. Displayed on 'hotpocket version' and written to new contract configs.
|
||||
constexpr const char *HP_VERSION = "0.1";
|
||||
|
||||
// Minimum compatible contract config version (this will be used to validate contract configs)
|
||||
constexpr const char *MIN_CONTRACT_VERSION = "0.1";
|
||||
// Minimum compatible config version (this will be used to validate contract configs)
|
||||
constexpr const char *MIN_CONFIG_VERSION = "0.1";
|
||||
|
||||
// Current version of the peer message protocol.
|
||||
constexpr uint8_t PEERMSG_VERSION = 1;
|
||||
|
||||
Reference in New Issue
Block a user