Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

Commit

Permalink
KEP-1761 - Ability to disable node-to-node encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
paularchard committed Oct 30, 2019
1 parent 76691ab commit dd03f1d
Show file tree
Hide file tree
Showing 24 changed files with 172 additions and 60 deletions.
1 change: 1 addition & 0 deletions crud/crud.cpp
Expand Up @@ -168,6 +168,7 @@ crud::send_response(const database_msg& request, const bzn::storage_result resul
}
else
{
LOG(trace) << "Sending response via session";
session->send_signed_message(std::make_shared<bzn_envelope>(env));
}
}
Expand Down
6 changes: 6 additions & 0 deletions mocks/mock_node_base.hpp
Expand Up @@ -38,6 +38,12 @@ class mock_node_base : public node_base {
void(const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr<bzn::encoded_message> msg));
MOCK_METHOD2(multicast_signed_message,
void(std::shared_ptr<std::vector<boost::asio::ip::tcp::endpoint>> eps, std::shared_ptr<bzn_envelope> msg));
MOCK_METHOD2(send_maybe_signed_message,
void(const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr<bzn_envelope> msg));
MOCK_METHOD2(send_maybe_signed_message,
void(const bzn::uuid_t& uuid, std::shared_ptr<bzn_envelope> msg));
MOCK_METHOD2(multicast_maybe_signed_message,
void(std::shared_ptr<std::vector<boost::asio::ip::tcp::endpoint>> eps, std::shared_ptr<bzn_envelope> msg));
};

} // namespace bzn
2 changes: 2 additions & 0 deletions mocks/mock_options_base.hpp
Expand Up @@ -86,6 +86,8 @@ class mock_options_base : public options_base {
std::string());
MOCK_CONST_METHOD0(get_admission_window,
size_t());
MOCK_CONST_METHOD0(get_peer_message_signing,
bool());
};

} // namespace bzn
4 changes: 2 additions & 2 deletions mocks/smart_mock_node.cpp
Expand Up @@ -32,12 +32,12 @@ bzn::smart_mock_node::smart_mock_node()
}
));

EXPECT_CALL(*this, multicast_signed_message(_, _)).WillRepeatedly(Invoke(
EXPECT_CALL(*this, multicast_maybe_signed_message(_, _)).WillRepeatedly(Invoke(
[&](auto endpoints, auto message)
{
for (const auto ep : *endpoints)
{
this->send_signed_message(ep, message);
this->send_maybe_signed_message(ep, message);
}
}));
}
Expand Down
46 changes: 45 additions & 1 deletion node/node.cpp
Expand Up @@ -84,7 +84,7 @@ void
node::register_error_handler(std::function<void(const boost::asio::ip::tcp::endpoint& ep
, const boost::system::error_code&)> error_handler)
{
this->error_callback = error_handler;
this->error_callback = std::move(error_handler);
}

void
Expand Down Expand Up @@ -274,6 +274,50 @@ node::send_signed_message(const bzn::uuid_t& uuid, std::shared_ptr<bzn_envelope>
}
}

void
node::send_maybe_signed_message(const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr<bzn_envelope> msg)
{
if (this->options->get_peer_message_signing())
{
this->find_session(ep)->send_signed_message(msg);
}
else
{
msg->set_sender(this->options->get_uuid());
msg->set_swarm_id(this->options->get_swarm_id());
this->find_session(ep)->send_message(std::make_shared<std::string>(msg->SerializeAsString()));
}
}

void
node::multicast_maybe_signed_message(std::shared_ptr<std::vector<boost::asio::ip::tcp::endpoint>> eps, std::shared_ptr<bzn_envelope> msg)
{
this->io_context->post(
[self = shared_from_this(), msg, eps]()
{
for (const auto& ep : *eps)
{
self->send_maybe_signed_message(ep, msg);
}
});
}

void
node::send_maybe_signed_message(const bzn::uuid_t& uuid, std::shared_ptr<bzn_envelope> msg)
{
try
{
auto point_of_contact_address = this->pbft->get_peer_by_uuid(uuid);
boost::asio::ip::tcp::endpoint endpoint{bzn::make_endpoint(point_of_contact_address)};
this->send_maybe_signed_message(endpoint, msg);
}
catch (const std::runtime_error& err)
{
LOG(error) << "Unable to send message to " << uuid << ": " << err.what();
}
}



void
node::initialize_ssl_contexts()
Expand Down
6 changes: 6 additions & 0 deletions node/node.hpp
Expand Up @@ -53,6 +53,12 @@ namespace bzn

void send_signed_message(const bzn::uuid_t& uuid, std::shared_ptr<bzn_envelope> msg) override;

void send_maybe_signed_message(const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr<bzn_envelope> msg) override;

void multicast_maybe_signed_message(std::shared_ptr<std::vector<boost::asio::ip::tcp::endpoint>> eps, std::shared_ptr<bzn_envelope> msg) override;

void send_maybe_signed_message(const bzn::uuid_t& uuid, std::shared_ptr<bzn_envelope> msg) override;

private:
FRIEND_TEST(node, test_that_registered_message_handler_is_invoked);
FRIEND_TEST(node, test_that_wrongly_signed_messages_are_dropped);
Expand Down
24 changes: 24 additions & 0 deletions node/node_base.hpp
Expand Up @@ -81,6 +81,30 @@ namespace bzn
*/
virtual void send_signed_message(const bzn::uuid_t& uuid, std::shared_ptr<bzn_envelope> msg) = 0;

/**
* Send a message to a node identified by endpoint. If the sender field is empty or contains our uuid, the message will be
* signed before sending. If the sender field contains something else, an existing signature will be kept intact.
* @param ep host to send the message to
* @param msg message to send
*/
virtual void send_maybe_signed_message(const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr<bzn_envelope> msg) = 0;

/**
* As send_signed_message, but send the same message to multiple recipients. The signature is computed only once
* and the whole operation is async.
* @param eps hosts to send the message to
* @param msg message to send
*/
virtual void multicast_maybe_signed_message(std::shared_ptr<std::vector<boost::asio::ip::tcp::endpoint>> eps, std::shared_ptr<bzn_envelope> msg) = 0;

/**
* Send a message to a node identified by uuid. If the sender field is empty or contains our uuid, the message will be
* signed before sending. If the sender field contains something else, an existing signature will be kept intact.
* @param uuid host to send the message to
* @param msg message to send
*/
virtual void send_maybe_signed_message(const bzn::uuid_t& uuid, std::shared_ptr<bzn_envelope> msg) = 0;

};

} // bzn
2 changes: 1 addition & 1 deletion node/test/node_test.cpp
Expand Up @@ -229,7 +229,7 @@ namespace bzn
auto mock_chaos = std::make_shared<NiceMock<bzn::mock_chaos_base>>();
auto mock_io_context = std::make_shared<NiceMock<bzn::asio::mock_io_context_base>>();
auto options = std::make_shared<bzn::options>();
options->get_mutable_simple_options).set(bzn::option_names::CRYPTO_ENABLED_INCOMING, "true");
options->get_mutable_simple_options().set(bzn::option_names::CRYPTO_ENABLED_INCOMING, "true");
auto monitor = std::make_shared<NiceMock<bzn::mock_monitor>>();
auto crypto = std::make_shared<bzn::crypto>(options, monitor);
auto mock_session = std::make_shared<bzn::mock_session_base>();
Expand Down
6 changes: 6 additions & 0 deletions options/options.cpp
Expand Up @@ -287,3 +287,9 @@ options::get_admission_window() const
{
return this->raw_opts.get<size_t>(ADMISSION_WINDOW);
}

bool
options::get_peer_message_signing() const
{
return this->raw_opts.get<bool>(PEER_MESSAGE_SIGNING);
}
2 changes: 2 additions & 0 deletions options/options.hpp
Expand Up @@ -84,6 +84,8 @@ namespace bzn

size_t get_admission_window() const override;

bool get_peer_message_signing() const override;

private:
size_t parse_size(const std::string& key) const;

Expand Down
6 changes: 6 additions & 0 deletions options/options_base.hpp
Expand Up @@ -245,5 +245,11 @@ namespace bzn
* @return size
*/
virtual size_t get_admission_window() const = 0;

/**
* Get whether we should sign/verify messages to peers
* @return true/false
*/
virtual bool get_peer_message_signing() const = 0;
};
} // bzn
5 changes: 4 additions & 1 deletion options/simple_options.cpp
Expand Up @@ -105,7 +105,10 @@ simple_options::build_options()
"software stack used by swarm")
(ADMISSION_WINDOW.c_str(),
po::value<size_t>()->default_value(30),
"admission control request window");
"admission control request window")
(PEER_MESSAGE_SIGNING.c_str(),
po::value<bool>()->default_value(false),
"should peer messages be signed/verified");

po::options_description logging("Logging");
logging.add_options()
Expand Down
1 change: 1 addition & 0 deletions options/simple_options.hpp
Expand Up @@ -50,6 +50,7 @@ namespace bzn::option_names
const std::string SIGNED_KEY = "signed_key";
const std::string OWNER_PUBLIC_KEY = "owner_public_key";
const std::string ADMISSION_WINDOW = "admission_window";
const std::string PEER_MESSAGE_SIGNING = "peer_message_signing";

const std::string CHAOS_ENABLED = "chaos_testing_enabled";
const std::string CHAOS_NODE_FAILURE_SHAPE = "chaos_node_failure_shape";
Expand Down
33 changes: 20 additions & 13 deletions pbft/pbft.cpp
Expand Up @@ -85,8 +85,13 @@ pbft::start()
this->node->register_for_message(bzn_envelope::kDatabaseResponse,
std::bind(&pbft::handle_database_response_message, shared_from_this(), std::placeholders::_1, std::placeholders::_2));

this->node->register_for_message(bzn_envelope::kSwarmError,
std::bind(&pbft::handle_swarm_error_response_message, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
this->node->register_for_message(bzn_envelope::kSwarmError, [weak_this = weak_from_this()](auto msg, auto session)
{
if (auto strong_this = weak_this.lock())
{
strong_this->handle_swarm_error_response_message(msg, session);
}
});

this->node->register_error_handler([weak_this = this->weak_from_this()](const boost::asio::ip::tcp::endpoint& ep, const boost::system::error_code& ec)
{
Expand Down Expand Up @@ -194,7 +199,7 @@ pbft::handle_membership_message(const bzn_envelope& msg, std::shared_ptr<bzn::se
return;
}

if ((!msg.sender().empty()) && (!this->crypto->verify(msg)))
if ((!msg.sender().empty()) && this->options->get_peer_message_signing() && (!this->crypto->verify(msg)))
{
LOG(error) << "Dropping message with invalid signature: " << msg.ShortDebugString().substr(0, MAX_MESSAGE_SIZE);
return;
Expand Down Expand Up @@ -242,7 +247,7 @@ pbft::handle_message(const pbft_msg& msg, const bzn_envelope& original_msg)
return;
}

if ((!original_msg.sender().empty()) && (!this->crypto->verify(original_msg)))
if ((!original_msg.sender().empty()) && this->options->get_peer_message_signing() && (!this->crypto->verify(original_msg)))
{
LOG(error) << "Dropping message with invalid signature: " << original_msg.ShortDebugString().substr(0, MAX_MESSAGE_SIZE);
return;
Expand Down Expand Up @@ -317,12 +322,11 @@ pbft::send_error_response(const bzn_envelope& request_env, const std::shared_ptr

bzn_envelope response;
response.set_swarm_error(err.SerializeAsString());

response.set_sender(this->uuid);
response.set_timestamp(this->now());
response.set_swarm_id(this->options->get_swarm_id());

return session->send_message(std::make_shared<std::string>(response.SerializeAsString()));
session->send_message(std::make_shared<std::string>(response.SerializeAsString()));
}
}

Expand Down Expand Up @@ -584,7 +588,7 @@ pbft::broadcast(const bzn_envelope& msg)

for (const auto& peer : *this->peers_beacon->current())
{
this->node->send_signed_message(make_endpoint(peer), msg_ptr);
this->node->send_maybe_signed_message(make_endpoint(peer), msg_ptr);
}
}

Expand Down Expand Up @@ -623,7 +627,7 @@ pbft::async_signed_broadcast(std::shared_ptr<bzn_envelope> msg_env)
targets->emplace_back(bzn::make_endpoint(peer));
}

this->node->multicast_signed_message(std::move(targets), msg_env);
this->node->multicast_maybe_signed_message(std::move(targets), msg_env);
}

void
Expand Down Expand Up @@ -1025,7 +1029,8 @@ pbft::validate_and_extract_checkpoint_hashes(const pbft_msg &viewchange_message)
const bzn_envelope& envelope{viewchange_message.checkpoint_messages(i)};
checkpoint_msg checkpoint_message;

if (!this->crypto->verify(envelope) || !this->is_peer(envelope.sender()) || !checkpoint_message.ParseFromString(envelope.checkpoint_msg()))
if ((this->options->get_peer_message_signing() && !this->crypto->verify(envelope))
|| !this->is_peer(envelope.sender()) || !checkpoint_message.ParseFromString(envelope.checkpoint_msg()))
{
LOG (error) << "Checkpoint validation failure - unable to verify envelope";
continue;
Expand All @@ -1046,7 +1051,8 @@ pbft::is_valid_prepared_proof(const prepared_proof& proof, uint64_t valid_checkp
{
const bzn_envelope& pre_prepare_envelope{proof.pre_prepare()};

if (!this->is_peer(pre_prepare_envelope.sender()) || !this->crypto->verify(pre_prepare_envelope))
if (!this->is_peer(pre_prepare_envelope.sender())
|| (this->options->get_peer_message_signing() && !this->crypto->verify(pre_prepare_envelope)))
{
LOG(error) << "is_valid_prepared_proof - a pre prepare message has a bad envelope, or the sender is not in the peers list";
LOG(error) << "Sender: " << pre_prepare_envelope.sender() << " is " << (this->is_peer(pre_prepare_envelope.sender()) ? "" : "not ") << "a peer";
Expand All @@ -1065,7 +1071,8 @@ pbft::is_valid_prepared_proof(const prepared_proof& proof, uint64_t valid_checkp
for (int j{0}; j < proof.prepare_size(); ++j)
{
bzn_envelope prepare_envelope{proof.prepare(j)};
if (!this->is_peer(prepare_envelope.sender()) || !this->crypto->verify(prepare_envelope))
if (!this->is_peer(prepare_envelope.sender()) ||
(this->options->get_peer_message_signing() && !this->crypto->verify(prepare_envelope)))
{
LOG(error) << "is_valid_prepared_proof - a prepare message has a bad envelope, "
"the sender may not be in the peer list, or the envelope failed cryptographic verification";
Expand Down Expand Up @@ -1233,7 +1240,7 @@ pbft::is_valid_newview_message(const pbft_msg& theirs, const bzn_envelope& origi

for (int i{0};i < theirs.pre_prepare_messages_size();++i)
{
if (!this->crypto->verify(theirs.pre_prepare_messages(i)))
if (this->options->get_peer_message_signing() && !this->crypto->verify(theirs.pre_prepare_messages(i)))
{
LOG(error) << "is_valid_newview_message - unable to verify thier pre prepare message";
return false;
Expand Down Expand Up @@ -1415,7 +1422,7 @@ pbft::save_checkpoint(const pbft_msg& msg)
{
const bzn_envelope& original_checkpoint{msg.checkpoint_messages(i)};

if (!this->crypto->verify(original_checkpoint))
if (this->options->get_peer_message_signing() && !this->crypto->verify(original_checkpoint))
{
LOG(error) << "ignoring invalid checkpoint message";
continue;
Expand Down
4 changes: 2 additions & 2 deletions pbft/pbft_checkpoint_manager.cpp
Expand Up @@ -88,7 +88,7 @@ pbft_checkpoint_manager::local_checkpoint_reached(const bzn::checkpoint_t& cp)
auto msg_ptr = std::make_shared<bzn_envelope>(msg);
for (const auto& peer : *(this->peers_beacon->current()))
{
this->node->send_signed_message(make_endpoint(peer), msg_ptr);
this->node->send_maybe_signed_message(make_endpoint(peer), msg_ptr);
}

if (cp.first == this->latest_local_checkpoint.value().first && cp.second != this->latest_local_checkpoint.value().second)
Expand Down Expand Up @@ -291,7 +291,7 @@ pbft_checkpoint_manager::send_state_request()
auto msg_ptr = std::make_shared<bzn_envelope>();
msg_ptr->set_pbft_membership(msg.SerializeAsString());

this->node->send_signed_message(selected_peer, msg_ptr);
this->node->send_maybe_signed_message(selected_peer, msg_ptr);
}

void
Expand Down
8 changes: 4 additions & 4 deletions pbft/test/pbft_audit_test.cpp
Expand Up @@ -17,9 +17,9 @@ namespace bzn::test
{
TEST_F(pbft_test, test_local_commit_sends_audit_messages)
{
EXPECT_CALL(*mock_node, send_signed_message(A<const boost::asio::ip::tcp::endpoint&>(), ResultOf(is_audit, Eq(false))))
EXPECT_CALL(*mock_node, send_maybe_signed_message(A<const boost::asio::ip::tcp::endpoint&>(), ResultOf(is_audit, Eq(false))))
.Times(AnyNumber());
EXPECT_CALL(*mock_node, send_signed_message(A<const boost::asio::ip::tcp::endpoint&>(), ResultOf(is_audit, Eq(true))))
EXPECT_CALL(*mock_node, send_maybe_signed_message(A<const boost::asio::ip::tcp::endpoint&>(), ResultOf(is_audit, Eq(true))))
.Times(Exactly(TEST_PEER_LIST.size()));

this->build_pbft();
Expand All @@ -46,7 +46,7 @@ namespace bzn::test

TEST_F(pbft_test, primary_sends_primary_status)
{
EXPECT_CALL(*mock_node, send_signed_message(A<const boost::asio::ip::tcp::endpoint&>(), ResultOf(is_audit, Eq(true))))
EXPECT_CALL(*mock_node, send_maybe_signed_message(A<const boost::asio::ip::tcp::endpoint&>(), ResultOf(is_audit, Eq(true))))
.Times(Exactly(TEST_PEER_LIST.size()));

this->build_pbft();
Expand All @@ -58,7 +58,7 @@ namespace bzn::test

TEST_F(pbft_test, nonprimary_does_not_send_primary_status)
{
EXPECT_CALL(*mock_node, send_signed_message(A<const boost::asio::ip::tcp::endpoint&>(), ResultOf(is_audit, Eq(true))))
EXPECT_CALL(*mock_node, send_maybe_signed_message(A<const boost::asio::ip::tcp::endpoint&>(), ResultOf(is_audit, Eq(true))))
.Times(Exactly(0));

this->uuid = SECOND_NODE_UUID;
Expand Down

0 comments on commit dd03f1d

Please sign in to comment.