From 2715ff7242f9924dc2cbb6ed73c54f377e66af92 Mon Sep 17 00:00:00 2001 From: Ravin Perera <33562092+ravinsp@users.noreply.github.com> Date: Mon, 28 Oct 2019 18:27:52 +0530 Subject: [PATCH] Restructured p2p message signing keys. --- src/cons/cons.cpp | 2 - src/p2p/message_container.fbs | 6 +- src/p2p/message_container_generated.h | 36 +++++- src/p2p/message_content.fbs | 4 +- src/p2p/message_content_generated.h | 50 ++------ src/p2p/peer_message_handler.cpp | 158 +++++++++++++------------- src/p2p/peer_message_handler.hpp | 8 +- src/p2p/peer_session_handler.cpp | 29 ++--- 8 files changed, 145 insertions(+), 148 deletions(-) diff --git a/src/cons/cons.cpp b/src/cons/cons.cpp index fb1a2781..e8ecfb8d 100644 --- a/src/cons/cons.cpp +++ b/src/cons/cons.cpp @@ -154,7 +154,6 @@ p2p::proposal create_stage0_proposal() // The proposal we are going to emit in stage 0. p2p::proposal stg_prop; stg_prop.time = ctx.time_now; - stg_prop.timestamp = ctx.time_now; ctx.novel_proposal_time = ctx.time_now; stg_prop.stage = 0; stg_prop.lcl = ctx.lcl; @@ -200,7 +199,6 @@ p2p::proposal create_stage123_proposal(vote_counter &votes) { // The proposal to be emited at the end of this stage. p2p::proposal stg_prop; - stg_prop.timestamp = ctx.time_now; stg_prop.stage = ctx.stage; // we always vote for our current lcl regardless of what other peers are saying diff --git a/src/p2p/message_container.fbs b/src/p2p/message_container.fbs index 1763cc78..0b14fab5 100644 --- a/src/p2p/message_container.fbs +++ b/src/p2p/message_container.fbs @@ -6,8 +6,10 @@ include "message_content.fbs"; namespace p2p; table Container { //root type for message - version: uint16; - signature:[ubyte]; + version:uint16; + timestamp:uint64; + pubkey:[ubyte]; + signature:[ubyte]; // signature of the message content content:[ubyte]; // message content: byte array of proposal,npl,etc } diff --git a/src/p2p/message_container_generated.h b/src/p2p/message_container_generated.h index 036e820d..459be17d 100644 --- a/src/p2p/message_container_generated.h +++ b/src/p2p/message_container_generated.h @@ -15,8 +15,10 @@ struct Container; struct Container FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_VERSION = 4, - VT_SIGNATURE = 6, - VT_CONTENT = 8 + VT_TIMESTAMP = 6, + VT_PUBKEY = 8, + VT_SIGNATURE = 10, + VT_CONTENT = 12 }; uint16_t version() const { return GetField(VT_VERSION, 0); @@ -24,6 +26,18 @@ struct Container FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool mutate_version(uint16_t _version) { return SetField(VT_VERSION, _version, 0); } + uint64_t timestamp() const { + return GetField(VT_TIMESTAMP, 0); + } + bool mutate_timestamp(uint64_t _timestamp) { + return SetField(VT_TIMESTAMP, _timestamp, 0); + } + const flatbuffers::Vector *pubkey() const { + return GetPointer *>(VT_PUBKEY); + } + flatbuffers::Vector *mutable_pubkey() { + return GetPointer *>(VT_PUBKEY); + } const flatbuffers::Vector *signature() const { return GetPointer *>(VT_SIGNATURE); } @@ -39,6 +53,9 @@ struct Container FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_VERSION) && + VerifyField(verifier, VT_TIMESTAMP) && + VerifyOffset(verifier, VT_PUBKEY) && + verifier.VerifyVector(pubkey()) && VerifyOffset(verifier, VT_SIGNATURE) && verifier.VerifyVector(signature()) && VerifyOffset(verifier, VT_CONTENT) && @@ -53,6 +70,12 @@ struct ContainerBuilder { void add_version(uint16_t version) { fbb_.AddElement(Container::VT_VERSION, version, 0); } + void add_timestamp(uint64_t timestamp) { + fbb_.AddElement(Container::VT_TIMESTAMP, timestamp, 0); + } + void add_pubkey(flatbuffers::Offset> pubkey) { + fbb_.AddOffset(Container::VT_PUBKEY, pubkey); + } void add_signature(flatbuffers::Offset> signature) { fbb_.AddOffset(Container::VT_SIGNATURE, signature); } @@ -74,11 +97,15 @@ struct ContainerBuilder { inline flatbuffers::Offset CreateContainer( flatbuffers::FlatBufferBuilder &_fbb, uint16_t version = 0, + uint64_t timestamp = 0, + flatbuffers::Offset> pubkey = 0, flatbuffers::Offset> signature = 0, flatbuffers::Offset> content = 0) { ContainerBuilder builder_(_fbb); + builder_.add_timestamp(timestamp); builder_.add_content(content); builder_.add_signature(signature); + builder_.add_pubkey(pubkey); builder_.add_version(version); return builder_.Finish(); } @@ -86,13 +113,18 @@ inline flatbuffers::Offset CreateContainer( inline flatbuffers::Offset CreateContainerDirect( flatbuffers::FlatBufferBuilder &_fbb, uint16_t version = 0, + uint64_t timestamp = 0, + const std::vector *pubkey = nullptr, const std::vector *signature = nullptr, const std::vector *content = nullptr) { + auto pubkey__ = pubkey ? _fbb.CreateVector(*pubkey) : 0; auto signature__ = signature ? _fbb.CreateVector(*signature) : 0; auto content__ = content ? _fbb.CreateVector(*content) : 0; return p2p::CreateContainer( _fbb, version, + timestamp, + pubkey__, signature__, content__); } diff --git a/src/p2p/message_content.fbs b/src/p2p/message_content.fbs index c6316372..063d2946 100644 --- a/src/p2p/message_content.fbs +++ b/src/p2p/message_content.fbs @@ -27,15 +27,13 @@ table Content { } table Proposal_Message { //Proposal type message schema - pubkey:[ubyte]; - timestamp:uint64; stage: int8; time:uint64; lcl:[ubyte]; users: [ByteArray]; raw_inputs: [RawInputList]; //stage 0 inputs (hash and raw value) hash_inputs:[ByteArray]; //stage > 0 inputs (hash of stage 0 inputs) - raw_outputs: [RawOutput]; //stage 0 outputs (hash and raw value) + raw_outputs: [RawOutput]; //stage 0 outputs (hash and raw value) hash_outputs:[ByteArray]; //stage > 0 outputs (hash of stage 0 outputs) state: State; } diff --git a/src/p2p/message_content_generated.h b/src/p2p/message_content_generated.h index dd0cfa4c..d032d9b1 100644 --- a/src/p2p/message_content_generated.h +++ b/src/p2p/message_content_generated.h @@ -411,30 +411,16 @@ inline flatbuffers::Offset CreateContent( struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_PUBKEY = 4, - VT_TIMESTAMP = 6, - VT_STAGE = 8, - VT_TIME = 10, - VT_LCL = 12, - VT_USERS = 14, - VT_RAW_INPUTS = 16, - VT_HASH_INPUTS = 18, - VT_RAW_OUTPUTS = 20, - VT_HASH_OUTPUTS = 22, - VT_STATE = 24 + VT_STAGE = 4, + VT_TIME = 6, + VT_LCL = 8, + VT_USERS = 10, + VT_RAW_INPUTS = 12, + VT_HASH_INPUTS = 14, + VT_RAW_OUTPUTS = 16, + VT_HASH_OUTPUTS = 18, + VT_STATE = 20 }; - const flatbuffers::Vector *pubkey() const { - return GetPointer *>(VT_PUBKEY); - } - flatbuffers::Vector *mutable_pubkey() { - return GetPointer *>(VT_PUBKEY); - } - uint64_t timestamp() const { - return GetField(VT_TIMESTAMP, 0); - } - bool mutate_timestamp(uint64_t _timestamp) { - return SetField(VT_TIMESTAMP, _timestamp, 0); - } int8_t stage() const { return GetField(VT_STAGE, 0); } @@ -491,9 +477,6 @@ struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_PUBKEY) && - verifier.VerifyVector(pubkey()) && - VerifyField(verifier, VT_TIMESTAMP) && VerifyField(verifier, VT_STAGE) && VerifyField(verifier, VT_TIME) && VerifyOffset(verifier, VT_LCL) && @@ -522,12 +505,6 @@ struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct Proposal_MessageBuilder { flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; - void add_pubkey(flatbuffers::Offset> pubkey) { - fbb_.AddOffset(Proposal_Message::VT_PUBKEY, pubkey); - } - void add_timestamp(uint64_t timestamp) { - fbb_.AddElement(Proposal_Message::VT_TIMESTAMP, timestamp, 0); - } void add_stage(int8_t stage) { fbb_.AddElement(Proposal_Message::VT_STAGE, stage, 0); } @@ -569,8 +546,6 @@ struct Proposal_MessageBuilder { inline flatbuffers::Offset CreateProposal_Message( flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset> pubkey = 0, - uint64_t timestamp = 0, int8_t stage = 0, uint64_t time = 0, flatbuffers::Offset> lcl = 0, @@ -582,7 +557,6 @@ inline flatbuffers::Offset CreateProposal_Message( flatbuffers::Offset state = 0) { Proposal_MessageBuilder builder_(_fbb); builder_.add_time(time); - builder_.add_timestamp(timestamp); builder_.add_state(state); builder_.add_hash_outputs(hash_outputs); builder_.add_raw_outputs(raw_outputs); @@ -590,15 +564,12 @@ inline flatbuffers::Offset CreateProposal_Message( builder_.add_raw_inputs(raw_inputs); builder_.add_users(users); builder_.add_lcl(lcl); - builder_.add_pubkey(pubkey); builder_.add_stage(stage); return builder_.Finish(); } inline flatbuffers::Offset CreateProposal_MessageDirect( flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *pubkey = nullptr, - uint64_t timestamp = 0, int8_t stage = 0, uint64_t time = 0, const std::vector *lcl = nullptr, @@ -608,7 +579,6 @@ inline flatbuffers::Offset CreateProposal_MessageDirect( const std::vector> *raw_outputs = nullptr, const std::vector> *hash_outputs = nullptr, flatbuffers::Offset state = 0) { - auto pubkey__ = pubkey ? _fbb.CreateVector(*pubkey) : 0; auto lcl__ = lcl ? _fbb.CreateVector(*lcl) : 0; auto users__ = users ? _fbb.CreateVector>(*users) : 0; auto raw_inputs__ = raw_inputs ? _fbb.CreateVector>(*raw_inputs) : 0; @@ -617,8 +587,6 @@ inline flatbuffers::Offset CreateProposal_MessageDirect( auto hash_outputs__ = hash_outputs ? _fbb.CreateVector>(*hash_outputs) : 0; return p2p::CreateProposal_Message( _fbb, - pubkey__, - timestamp, stage, time, lcl__, diff --git a/src/p2p/peer_message_handler.cpp b/src/p2p/peer_message_handler.cpp index 77708bcf..5f92091e 100644 --- a/src/p2p/peer_message_handler.cpp +++ b/src/p2p/peer_message_handler.cpp @@ -48,26 +48,87 @@ int validate_and_extract_container(const Container **container_ref, std::string_ //Verify container message using flatbuffer verifier if (!VerifyContainerBuffer(container_verifier)) { - LOG_DBG << "Flatbuffer verify: Bad container."; + LOG_DBG << "Flatbuffer verify: Bad peer message container."; return -1; } //Get message container const Container *container = GetContainer(container_buf_ptr); + //Validation are prioritzed base on expensiveness of validation. + //i.e - signature validation is done at the end. + //check protocol version of message whether it is greater than minimum supported protocol version. const uint16_t version = container->version(); if (version < util::MIN_PEERMSG_VERSION) { - LOG_DBG << "Recieved message is from unsupported protocol version (" << version << ")"; + LOG_DBG << "Peer message is from unsupported protocol version (" << version << ")."; return -1; } + int64_t time_now = util::get_epoch_milliseconds(); + + //check message timestamp. + if (container->timestamp() < (time_now - conf::cfg.roundtime * 4)) + { + LOG_DBG << "Peer message is too old."; + return -1; + } + + // After signature is verified, get message hash and see wheteher + // message is already recieved -> abandon if duplicate. + // auto messageHash = crypto::sha_512_hash(message, "PEERMSG", 7); + + // if (recent_peer_msghash.count(messageHash) == 0) + // { + // recent_peer_msghash.try_emplace(std::move(messageHash), timestamp); + // } + // else + // { + // LOG_DBG << "Duplicate message"; + // return -1; + // } + //Assign container and content out params. *container_ref = container; return 0; } +/** + * Validates the container message signing keys to see if the message is from a trusted source (UNL). + * @return 0 on successful verification. -1 for failure. + */ +int validate_container_trust(const Container *container) +{ + std::string_view msg_pubkey = flatbuff_bytes_to_sv(container->pubkey()); + std::string_view msg_sig = flatbuff_bytes_to_sv(container->signature()); + + if (msg_pubkey.empty() || msg_sig.empty()) + { + LOG_DBG << "Peer message key pair incomplete. Trust verification failed."; + return -1; + } + + //validate if the message is not from a node of current node's unl list. + if (!conf::cfg.unl.count(std::string(msg_pubkey))) + { + LOG_DBG << "Peer message pubkey verification failed. Not in UNL."; + return -1; + } + + //verify message signature. + //this is performed towards end since this is bit expensive + std::string_view msg_content = flatbuff_bytes_to_sv(container->content()); + + if (crypto::verify(msg_content, msg_sig, msg_pubkey) != 0) + { + LOG_DBG << "Peer message signature verification failed."; + return -1; + } + + return 0; +} + /** * Verifies the Content message structure and outputs faltbuffer Content pointer to access the given buffer. * @@ -94,81 +155,17 @@ int validate_and_extract_content(const Content **content_ref, const uint8_t *con return 0; } -/** - * Validate the incoming p2p message content on several criteria. - * - * @param message Message content data buffer. - * @param signature Binary message signature. - * @param pubkey Binary public key of message originating node. - * @param timestamp Message timestamp. - * @param version Message protocol version. - * @return 0 on successful validation. -1 for failure. - */ -int validate_content_message(std::string_view message, std::string_view signature, std::string_view pubkey, int64_t timestamp) -{ - //Validation are prioritzed base on expensiveness of validation. - //i.e - signature validation is done at the end. - - int64_t time_now = util::get_epoch_milliseconds(); - - // validate if the message is not from a node of current node's unl list. - if (!conf::cfg.unl.count(pubkey.data())) - { - LOG_DBG << "pubkey verification failed"; - return -1; - } - - //check message timestamp. < timestamp now - 4* round time. - /*todo:this might change to check only current stage related. (Base on how consensus algorithm implementation take shape) - check message stage is for valid stage(node's current consensus stage - 1) - */ - if (timestamp < (time_now - conf::cfg.roundtime * 4)) - { - LOG_DBG << "Recieved message from peer is old"; - return -1; - } - - //verify message signature. - //this should be the last validation since this is bit expensive - auto signature_verified = crypto::verify(message, signature, pubkey); - - if (signature_verified != 0) - { - LOG_DBG << "Signature verification failed"; - return -1; - } - - // After signature is verified, get message hash and see wheteher - // message is already recieved -> abandon if duplicate. - // auto messageHash = crypto::sha_512_hash(message, "PEERMSG", 7); - - // if (recent_peer_msghash.count(messageHash) == 0) - // { - // recent_peer_msghash.try_emplace(std::move(messageHash), timestamp); - // } - // else - // { - // LOG_DBG << "Duplicate message"; - // return -1; - // } - - return 0; -} - /** * Creates a proposal stuct from the given proposal message. * @param The Flatbuffer poporal received from the peer. * @return A proposal struct representing the message. */ -const proposal create_proposal_from_msg(const Proposal_Message &msg) +const proposal create_proposal_from_msg(const Proposal_Message &msg, const flatbuffers::Vector *pubkey) { proposal p; - if (msg.pubkey()) - p.pubkey = flatbuff_bytes_to_sv(msg.pubkey()); - + p.pubkey = flatbuff_bytes_to_sv(pubkey); p.time = msg.time(); - p.timestamp = msg.timestamp(); p.stage = msg.stage(); if (msg.lcl()) @@ -208,8 +205,6 @@ void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder, flatbuffers::Offset proposal = CreateProposal_Message( builder, - sv_to_flatbuff_bytes(builder, conf::cfg.pubkey), - p.timestamp, p.stage, p.time, sv_to_flatbuff_bytes(builder, p.lcl), @@ -224,7 +219,7 @@ void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder, // Now that we have built the content message, // we need to sign it and place it inside a container message. - create_containermsg_from_content(container_builder, builder); + create_containermsg_from_content(container_builder, builder, true); } /** @@ -232,9 +227,10 @@ void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder, * @param container_builder The Flatbuffer builder to which the final container message should be written to. * @param content_builder The Flatbuffer builder containing the content message that should be placed * inside the container message. + * @param sign Whether to sign the message content. */ void create_containermsg_from_content( - flatbuffers::FlatBufferBuilder &container_builder, const flatbuffers::FlatBufferBuilder &content_builder) + flatbuffers::FlatBufferBuilder &container_builder, const flatbuffers::FlatBufferBuilder &content_builder, bool sign) { uint8_t *content_buf = content_builder.GetBufferPointer(); flatbuffers::uoffset_t content_size = content_builder.GetSize(); @@ -242,14 +238,24 @@ void create_containermsg_from_content( // Create container message content from serialised content from previous step. flatbuffers::Offset> content = container_builder.CreateVector(content_buf, content_size); - // Sign message content with this node's private key. - std::string_view content_to_sign(reinterpret_cast(content_buf), content_size); - std::string sig = crypto::sign(content_to_sign, conf::cfg.seckey); + flatbuffers::Offset> pubkey_offset = 0; + flatbuffers::Offset> sig_offset = 0; + + if (sign) + { + // Sign message content with this node's private key. + std::string_view content_to_sign(reinterpret_cast(content_buf), content_size); + + sig_offset = sv_to_flatbuff_bytes(container_builder, crypto::sign(content_to_sign, conf::cfg.seckey)); + pubkey_offset = sv_to_flatbuff_bytes(container_builder, conf::cfg.pubkey); + } flatbuffers::Offset container_message = CreateContainer( container_builder, util::PEERMSG_VERSION, - sv_to_flatbuff_bytes(container_builder, sig), //signature field + util::get_epoch_milliseconds(), + pubkey_offset, + sig_offset, content); // Finish building message container to get serialised message. diff --git a/src/p2p/peer_message_handler.hpp b/src/p2p/peer_message_handler.hpp index 06e39431..e7f2fb41 100644 --- a/src/p2p/peer_message_handler.hpp +++ b/src/p2p/peer_message_handler.hpp @@ -17,18 +17,18 @@ namespace p2p int validate_and_extract_container(const Container **container_ref, std::string_view container_buf); +int validate_container_trust(const Container *container); + int validate_and_extract_content(const Content **content_ref, const uint8_t *content_ptr, flatbuffers::uoffset_t content_size); -int validate_content_message(std::string_view message, std::string_view signature, std::string_view pubkey, int64_t timestamp); - -const proposal create_proposal_from_msg(const Proposal_Message &msg); +const proposal create_proposal_from_msg(const Proposal_Message &msg, const flatbuffers::Vector *pubkey); //---Message creation helpers---// void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder, const proposal &p); void create_containermsg_from_content( - flatbuffers::FlatBufferBuilder &container_builder, const flatbuffers::FlatBufferBuilder &content_builder); + flatbuffers::FlatBufferBuilder &container_builder, const flatbuffers::FlatBufferBuilder &content_builder, bool sign); //---Conversion helpers from flatbuffers data types to std data types---// diff --git a/src/p2p/peer_session_handler.cpp b/src/p2p/peer_session_handler.cpp index 0b09d8d9..4efd81f5 100644 --- a/src/p2p/peer_session_handler.cpp +++ b/src/p2p/peer_session_handler.cpp @@ -12,7 +12,7 @@ namespace p2p { - + peer_outbound_message::peer_outbound_message( std::shared_ptr _fbbuilder_ptr) { @@ -82,24 +82,17 @@ void peer_session_handler::on_message(sock::socket_sessionmessage_as_Proposal_Message(); - - //validate message for malleability, timeliness, signature and prune recieving messages. - bool val_result = validate_content_message( - flatbuff_bytes_to_sv(content_ptr, content_size), - flatbuff_bytes_to_sv(container->signature()), - flatbuff_bytes_to_sv(proposalmsg->pubkey()), - proposalmsg->timestamp()); + // We only trust proposals coming from trusted peers. + if (validate_container_trust(container) != 0) + { + LOG_DBG << "Proposal rejected due to trust failure."; + return; + } - if (val_result == 0) - { - std::lock_guard lock(collected_msgs.proposals_mutex); - collected_msgs.proposals.push_back(create_proposal_from_msg(*proposalmsg)); - } - else - { - LOG_DBG << "Message content field validation failed"; - } + std::lock_guard lock(collected_msgs.proposals_mutex); // Insert proposal with lock. + + collected_msgs.proposals.push_back( + create_proposal_from_msg(*content->message_as_Proposal_Message(), container->pubkey())); } else if (content_message_type == Message_Npl_Message) //message is a NPL message {