#include "node.hpp" #include "cmix.h" #include "bignum.h" #include "logging.hpp" #include using namespace boost::asio::ip; Node::Node(ListenSettings const& listen_settings, NodeNetworkSettings network_settings) : io_service() , ssl_ctx(std::make_shared(boost::asio::ssl::context::sslv23)) , server(io_service, listen_settings, ssl_ctx, [this](std::unique_ptr>&& socket, std::shared_ptr ctx){accept_handler(std::move(socket), ctx);}) , clients() , data() , network_settings(network_settings) , prev_node(SSLReceiver(std::unique_ptr>(new boost::asio::ssl::stream(io_service, *ssl_ctx)))) , next_node(SSLSender(std::unique_ptr>(new boost::asio::ssl::stream(io_service, *ssl_ctx)))) , cmix_ctx(initialize_cmix_context(get_implementation())) , precomputation_data() , shutting_down(false) { initialize_keypair(&cmix_ctx); GOOGLE_PROTOBUF_VERIFY_VERSION; if(network_settings.is_first) { connect_to_next_node(); } } Node::~Node() { deinitialize(&cmix_ctx); } void Node::run() { io_service.run(); } void Node::accept_handler(std::unique_ptr>&& socket, std::shared_ptr ctx) { Purgatory::iterator it = purgatory.emplace(purgatory.end(), std::move(socket)); purgatory.back().on_done( [this, it]() { purgatory.erase(it); } ); it->async_receive([this, it](cmix_proto::CMixMessage message) { handle_message(it, message); }); } void Node::connect_to_next_node() { if(!network_settings.certdir.empty()) { ssl_ctx->add_verify_path(network_settings.certdir); } auto on_connect = [this](){ BOOST_LOG_TRIVIAL(trace) << "Connected to next_node"; next_node.async_send(cmix_proto::ImANode()); }; next_node.async_connect(network_settings.next_host, network_settings.next_port, on_connect); } void Node::start_initialisation() { cmix_proto::Initialization init; size_t len = get_group_element_array_size(&cmix_ctx); init.mutable_public_share()->resize(len); get_public_key(&cmix_ctx, &(*init.mutable_public_share())[0]); BOOST_LOG_TRIVIAL(trace) << "Sending intialization as first node"; next_node.async_send(init); } void Node::handle_node_initialization(const cmix_proto::Initialization& init) { if(network_settings.is_first) { set_network_key(&cmix_ctx, init.public_share().data(), init.public_share().size()); cmix_proto::SecretKey sec; sec.set_secret_key(init.public_share().data(), init.public_share().size()); next_node.async_send(sec); } else { size_t len = get_group_element_array_size(&cmix_ctx); cmix_proto::Initialization n_init; n_init.mutable_public_share()->resize(len); add_public_share(&cmix_ctx, &(*n_init.mutable_public_share())[0], init.public_share().data()); BOOST_LOG_TRIVIAL(trace) << "Sending intialization"; next_node.async_send(n_init); } } void Node::handle_node_secretkey(cmix_proto::SecretKey const& secret) { std::string share = secret.secret_key(); set_network_key(&cmix_ctx, secret.secret_key().data(), secret.secret_key().size()); if(network_settings.is_first) { start_precomputation(); } else { next_node.async_send(secret); } } template cmix_proto::PrePre fill_precomputation_pre_message(CMixContext& ctx, T const& rs, T const& ms) { if(start_mix(&ctx, rs.size()) != no_error) { exit(-1); } if(initialize_mix_randomness(&ctx) != no_error) { exit(-1); } cmix_proto::PrePre prepre; for(size_t i = 0; i < ctx.nr_participants; ++i) { size_t len = get_group_element_array_size(&ctx); prepre.mutable_m_er(i)->resize(len); prepre.mutable_r_er(i)->resize(len); if(encrypt_r_and_multiply( &ctx, &(*prepre.mutable_r_er(i))[0], &(*prepre.mutable_m_er(i))[0], rs.Get(i).data(), ms.Get(i).data(), i ) != no_error) { exit(-1); } } return prepre; } template cmix_proto::PreMix fill_precomputation_mix_message(CMixContext const& ctx, T const& rs, T const& ms) { cmix_proto::PreMix premix; for(size_t i = 0; i < ctx.nr_participants; ++i) { auto new_pos = ctx.permutation[i]; size_t el_len = get_group_element_array_size(&ctx); premix.mutable_r_epirs(new_pos)->resize(el_len); premix.mutable_m_epirs(new_pos)->resize(el_len); multiply_s( &ctx, &(*premix.mutable_r_epirs(new_pos))[0], &(*premix.mutable_m_epirs(new_pos))[0], rs.Get(i).data(), ms.Get(i).data(), i ); } return premix; } template cmix_proto::PrePost fill_precomputation_post_message(CMixContext& ctx, T const& rs, T const& ms) { cmix_proto::PrePost prepost; for(size_t i = 0; i < ctx.nr_participants; ++i) { post_process(&ctx, rs.Get(i).data(), ms.Get(i).data(), i); *prepost.mutable_r_epirs(i) = rs.Get(i); *prepost.mutable_m_epirs(i) = ms.Get(i); } return prepost; } void Node::handle_node_prepre(cmix_proto::PrePre const& pre) { if(network_settings.is_first) { cmix_proto::PreMix premix = fill_precomputation_mix_message(cmix_ctx, pre.r_er(), pre.m_er()); next_node.async_send(premix); } else { cmix_proto::PrePre prepre = fill_precomputation_pre_message(cmix_ctx, pre.r_er(), pre.m_er()); next_node.async_send(prepre); } } void Node::handle_node_premix(cmix_proto::PreMix const& premix) { if(network_settings.is_first) { cmix_proto::PrePost prepost = fill_precomputation_post_message(cmix_ctx, premix.r_epirs(), premix.m_epirs()); next_node.async_send(prepost); } else { cmix_proto::PreMix n_premix = fill_precomputation_mix_message(cmix_ctx, premix.r_epirs(), premix.m_epirs()); next_node.async_send(n_premix); } } void Node::handle_node_prepost(cmix_proto::PrePost const& prepost) { if(network_settings.is_first) { } else { cmix_proto::PrePost n_prepost = fill_precomputation_post_message(cmix_ctx, prepost.r_epirs(), prepost.m_epirs()); next_node.async_send(n_prepost); } } void Node::handle_node_message(cmix_proto::CMixMessage message) { switch(message.contents_case()) { case cmix_proto::CMixMessage::ContentsCase::kInitialization: { BOOST_LOG_TRIVIAL(trace) << "Handling initialization"; handle_node_initialization(message.initialization()); break; } case cmix_proto::CMixMessage::ContentsCase::kBye: { BOOST_LOG_TRIVIAL(trace) << "Handling bye"; //Todo: find a nice way to handle network shutdown. break; } case cmix_proto::CMixMessage::ContentsCase::kSecretkey: { BOOST_LOG_TRIVIAL(trace) << "Handling SecretKey"; handle_node_secretkey(message.secretkey()); break; } case cmix_proto::CMixMessage::ContentsCase::kPrepre: { BOOST_LOG_TRIVIAL(trace) << "Handling PrePre"; handle_node_prepre(message.prepre()); break; } case cmix_proto::CMixMessage::ContentsCase::kPremix: { BOOST_LOG_TRIVIAL(trace) << "Handling PreMix"; handle_node_premix(message.premix()); break; } case cmix_proto::CMixMessage::ContentsCase::kPrepost: { BOOST_LOG_TRIVIAL(trace) << "Handling PrePost"; handle_node_prepost(message.prepost()); break; } default: { BOOST_LOG_TRIVIAL(error) << "handle_node_message: CMixMessage contains unknown contents."; } } prev_node.async_receive([this](cmix_proto::CMixMessage message) { handle_node_message(message); }); } void Node::handle_client_keyexchange(ClientConnections::key_type handle, cmix_proto::KeyExchange ke) { CMixClientData d; size_t len = get_group_element_array_size(&cmix_ctx); cmix_proto::KeyExchange nke; nke.mutable_public_key()->resize(len); nke.mutable_value()->resize(len); key_exchange(&cmix_ctx, &d.shared_value.shared, &(*nke.mutable_public_key())[0], &(*nke.mutable_value())[0], ke.public_key().data(), ke.value().data()); data[handle] = d; clients.at(handle).async_send(nke); } void Node::handle_client_bye(ClientConnections::key_type handle, cmix_proto::Bye) { clients.at(handle).close(); clients.erase(handle); } void Node::handle_client_message(ClientConnections::key_type handle, cmix_proto::CMixMessage message) { switch(message.contents_case()) { case cmix_proto::CMixMessage::ContentsCase::kKeyexchange: { BOOST_LOG_TRIVIAL(trace) << "Handling keyexchange"; handle_client_keyexchange(handle, message.keyexchange()); break; } case cmix_proto::CMixMessage::ContentsCase::kBye: { BOOST_LOG_TRIVIAL(trace) << "Handling bye"; handle_client_bye(handle, message.bye()); return; } default: { BOOST_LOG_TRIVIAL(error) << "handle_client_message: CMixMessage contains unknown contents."; } } clients.at(handle).async_receive([this, handle](cmix_proto::CMixMessage message){ handle_client_message(handle, message); }); } void Node::handle_imanode(Purgatory::iterator handle) { handle->on_done([]{}); prev_node = std::move(*handle); purgatory.erase(handle); if(network_settings.is_first) { start_initialisation(); } else { connect_to_next_node(); } prev_node.async_receive([this](cmix_proto::CMixMessage message){ handle_node_message(message); }); } void Node::handle_imaclient(Purgatory::iterator handle, cmix_proto::ImAClient c) { std::string client_id = c.id(); clients.emplace(c.id(), std::move(*handle)); clients.at(c.id()).on_done([this, client_id]{ clients.erase(client_id); }); purgatory.erase(handle); clients.at(c.id()).async_send(cmix_proto::NodeReady()); clients.at(c.id()).async_receive([this, client_id](cmix_proto::CMixMessage message) { handle_client_message(client_id, message); }); } void Node::handle_message(Purgatory::iterator handle, cmix_proto::CMixMessage message) { switch(message.contents_case()) { case cmix_proto::CMixMessage::ContentsCase::kImanode: { BOOST_LOG_TRIVIAL(trace) << "Handling imanode"; handle_imanode(handle); return; } case cmix_proto::CMixMessage::ContentsCase::kImaclient: { BOOST_LOG_TRIVIAL(trace) << "Handling imaclient"; handle_imaclient(handle, message.imaclient()); return; } default: { BOOST_LOG_TRIVIAL(error) << "handle_message: CMixMessage contains unknown contents."; } } handle->close(); purgatory.erase(handle); } void Node::start_precomputation() { if(start_mix(&cmix_ctx, clients.size()) != no_error) { exit(-1); } if(initialize_mix_randomness(&cmix_ctx) != no_error) { exit(-1); } cmix_proto::PrePre prepre; for(size_t i = 0; i < cmix_ctx.nr_participants; ++i) { size_t len = get_group_element_array_size(&cmix_ctx); prepre.mutable_r_er(i)->resize(len); prepre.mutable_m_er(i)->resize(len); if(encrypt_r(&cmix_ctx, &(*prepre.mutable_r_er(i))[0], &(*prepre.mutable_m_er(i))[0], i) != no_error) { exit(-1); } } next_node.async_send(prepre); }