#include #include #include "../App/appconfig.hpp" // The next line suppresses a deprecation warning within boost #define BOOST_BIND_GLOBAL_PLACEHOLDERS #include "boost/property_tree/ptree.hpp" #include "boost/property_tree/json_parser.hpp" #include #include #include "gcm.h" #include "sgx_tcrypto.h" #include "clients.hpp" #define CEILDIV(x,y) (((x)+(y)-1)/(y)) Config config; Client *clients; aes_key ESK, TSK; std::vector ingestion_nodes, storage_nodes; std::vector storage_map; std::vector ingestion_map; unsigned long setup_time; // Split a hostport string like "127.0.0.1:12000" at the rightmost colon // into a host part "127.0.0.1" and a port part "12000". static bool split_host_port(std::string &host, std::string &port, const std::string &hostport) { size_t colon = hostport.find_last_of(':'); if (colon == std::string::npos) { std::cerr << "Cannot parse \"" << hostport << "\" as host:port\n"; return false; } host = hostport.substr(0, colon); port = hostport.substr(colon+1); return true; } // Convert a single hex character into its value from 0 to 15. Return // true on success, false if it wasn't a hex character. static inline bool hextoval(unsigned char &val, char hex) { if (hex >= '0' && hex <= '9') { val = ((unsigned char)hex)-'0'; } else if (hex >= 'a' && hex <= 'f') { val = ((unsigned char)hex)-'a'+10; } else if (hex >= 'A' && hex <= 'F') { val = ((unsigned char)hex)-'A'+10; } else { return false; } return true; } // Convert a 2*len hex character string into a len-byte buffer. Return // true on success, false on failure. static bool hextobuf(unsigned char *buf, const char *str, size_t len) { if (strlen(str) != 2*len) { std::cerr << "Hex string was not the expected size\n"; return false; } for (size_t i=0;i &ingestion_nodes, std::vector &storage_nodes, std::vector &storage_map) { bool found_params = false; bool ret = true; std::istringstream configstream(configstr); boost::property_tree::ptree conftree; read_json(configstream, conftree); uint16_t node_num = 0; for (auto & entry : conftree) { if (!entry.first.compare("params")) { for (auto & pentry : entry.second) { if (!pentry.first.compare("msg_size")) { config.msg_size = pentry.second.get_value(); } else if (!pentry.first.compare("user_count")) { config.user_count = pentry.second.get_value(); } else if (!pentry.first.compare("priv_out")) { config.m_priv_out = pentry.second.get_value(); } else if (!pentry.first.compare("priv_in")) { config.m_priv_in = pentry.second.get_value(); } else if (!pentry.first.compare("pub_out")) { config.m_pub_out = pentry.second.get_value(); } else if (!pentry.first.compare("pub_in")) { config.m_pub_in = pentry.second.get_value(); // A hardcoded shared secret to derive various // keys for client -> server communications and tokens } else if (!pentry.first.compare("master_secret")) { std::string hex_key = pentry.second.data(); memcpy(config.master_secret, hex_key.c_str(), SGX_AESGCM_KEY_SIZE); } else { std::cerr << "Unknown field in params: " << pentry.first << "\n"; ret = false; } } found_params = true; } else if (!entry.first.compare("nodes")) { for (auto & node : entry.second) { NodeConfig nc; // All nodes need to be assigned their role in manifest.yaml nc.roles = 0; for (auto & nentry : node.second) { if (!nentry.first.compare("name")) { nc.name = nentry.second.get_value(); } else if (!nentry.first.compare("pubkey")) { ret &= hextobuf((unsigned char *)&nc.pubkey, nentry.second.get_value().c_str(), sizeof(nc.pubkey)); } else if (!nentry.first.compare("weight")) { nc.weight = nentry.second.get_value(); } else if (!nentry.first.compare("listen")) { ret &= split_host_port(nc.listenhost, nc.listenport, nentry.second.get_value()); } else if (!nentry.first.compare("clisten")) { ret &= split_host_port(nc.clistenhost, nc.clistenport, nentry.second.get_value()); } else if (!nentry.first.compare("slisten")) { ret &= split_host_port(nc.slistenhost, nc.slistenport, nentry.second.get_value()); } else if (!nentry.first.compare("roles")) { nc.roles = nentry.second.get_value(); } else { std::cerr << "Unknown field in host config: " << nentry.first << "\n"; ret = false; } } if(nc.roles & ROLE_INGESTION) { ingestion_nodes.push_back(nc); ingestion_map.push_back(node_num); } if(nc.roles & ROLE_STORAGE) { storage_nodes.push_back(std::move(nc)); storage_map.push_back(node_num); } node_num++; } } else { std::cerr << "Unknown key in config: " << entry.first << "\n"; ret = false; } } if (!found_params) { std::cerr << "Could not find params in config\n"; ret = false; } return ret; } static void usage(const char *argv0) { fprintf(stderr, "%s [-t nthreads] < config.json\n", argv0); exit(1); } /* Generate ESK (Encryption Secret Key) and TSK (Token Secret Key) */ int generateMasterKeys(sgx_aes_gcm_128bit_key_t master_secret, aes_key &ESK, aes_key &TSK ) { unsigned char zeroes[SGX_AESGCM_KEY_SIZE]; unsigned char iv[SGX_AESGCM_IV_SIZE]; unsigned char mac[SGX_AESGCM_MAC_SIZE]; memset(iv, 0, SGX_AESGCM_IV_SIZE); memset(zeroes, 0, SGX_AESGCM_KEY_SIZE); memcpy(iv, "Encryption", sizeof("Encryption")); if (sizeof(zeroes) != gcm_encrypt(zeroes, SGX_AESGCM_KEY_SIZE, NULL, 0, master_secret, iv, SGX_AESGCM_IV_SIZE, ESK, mac)) { printf("Client: generateMasterKeys FAIL\n"); return -1; } printf("\n\nEncryption Master Key: "); for(int i=0;i ing_with_additional && ing_with_additional!=0) { uint16_t leftover = num_clients_total - (ing_with_additional * clients_per_ing); ing_no = ing_with_additional + (leftover / (clients_per_ing-1)); } uint16_t stg_no = i % num_stg_nodes; uint16_t stg_node_id = storage_map[stg_no]; uint16_t ing_node_id = ingestion_map[ing_no]; clients[i].setup_client(io_context, i, ing_node_id, stg_node_id); } } /* Epochs are server driven. In a single epoch, each client waits to receive from their storage server (i) a token bundle for this epoch and (ii) their messages from the last epoch The client then sends their messages for this epoch to their ingestion servers using the tokens they received in this epoch */ void Client::epoch_process() { uint32_t pt_token_size = (config.m_priv_out * SGX_AESGCM_KEY_SIZE); uint32_t token_bundle_size = pt_token_size + SGX_AESGCM_IV_SIZE + SGX_AESGCM_MAC_SIZE; unsigned char *enc_tokens = (unsigned char*) malloc (token_bundle_size); //Async read the encrypted tokens for this epoch boost::asio::async_read(*storage_sock, boost::asio::buffer(enc_tokens, token_bundle_size), [this, enc_tokens, token_bundle_size, pt_token_size] (boost::system::error_code ec, std::size_t) { if (ec) { if(ec == boost::asio::error::eof) { delete(storage_sock); } else { printf("Error %s\n", ec.message().c_str()); } printf("Client::epoch_process boost async_read_tokens failed\n"); return; } /* if(sim_id == 0) { printf("TEST: Client 0: Encrypted token bundle received:\n"); for(uint32_t i = 0; i < token_bundle_size; i++) { printf("%x", enc_tokens[i]); } printf("\n"); } */ // Decrypt the token bundle unsigned char *enc_tkn_ptr = enc_tokens + SGX_AESGCM_IV_SIZE; unsigned char *enc_tkn_tag = enc_tokens + SGX_AESGCM_IV_SIZE + pt_token_size; int decrypted_bytes = gcm_decrypt(enc_tkn_ptr, pt_token_size, NULL, 0, enc_tkn_tag, (unsigned char*) &(this->stg_key), enc_tokens, SGX_AESGCM_IV_SIZE, (unsigned char*) (this->token_list)); if(decrypted_bytes != pt_token_size) { printf("Client::epoch_process gcm_decrypt tokens failed \n"); } unsigned char *tkn_ptr = (unsigned char*) this->token_list; free(enc_tokens); /* if(sim_id==0) { printf("TEST: Client 0: Decrypted client tokens:\n"); for(int i = 0; i < 2 * SGX_AESGCM_KEY_SIZE; i++) { printf("%x", tkn_ptr[i]); } printf("\n"); } */ // Async read the messages recieved in the last epoch uint16_t priv_in = config.m_priv_in; uint16_t msg_size = config.msg_size; uint32_t recv_pt_msgbundle_size = ptMsgBundleSize(priv_in, msg_size); uint32_t recv_enc_msgbundle_size = encMsgBundleSize(priv_in, msg_size); unsigned char *recv_pt_msgbundle = (unsigned char*) malloc (recv_pt_msgbundle_size); unsigned char *recv_enc_msgbundle = (unsigned char*) malloc (recv_enc_msgbundle_size); boost::asio::async_read(*storage_sock, boost::asio::buffer(recv_enc_msgbundle, recv_enc_msgbundle_size), [this, recv_pt_msgbundle, recv_enc_msgbundle] (boost::system::error_code ecc, std::size_t) { if (ecc) { if(ecc == boost::asio::error::eof) { delete(storage_sock); } else { printf("Error %s\n", ecc.message().c_str()); } printf("Client: boost async_read failed for recieving msg_bundle\n"); return; } // Do whatever processing with the received messages here free(recv_enc_msgbundle); free(recv_pt_msgbundle); // Send this epoch's message bundle sendMessageBundle(); }); epoch_process(); }); } void client_epoch_process(uint32_t cstart, uint32_t cstop) { for(uint32_t i=cstart; i threads; uint32_t num_clients_total = config.user_count; size_t clients_per_thread = CEILDIV(num_clients_total, nthreads); // Generate all the clients for the experiment for(int i=0; i threads; for(int i=0; i