clients.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. #include <iostream>
  2. #include "../App/appconfig.hpp"
  3. // The next line suppresses a deprecation warning within boost
  4. #define BOOST_BIND_GLOBAL_PLACEHOLDERS
  5. #include "boost/property_tree/ptree.hpp"
  6. #include "boost/property_tree/json_parser.hpp"
  7. #include <boost/asio.hpp>
  8. #include <boost/thread.hpp>
  9. #include "gcm.h"
  10. #include "sgx_tcrypto.h"
  11. #include "clients.hpp"
  12. #define CEILDIV(x,y) (((x)+(y)-1)/(y))
  13. // Split a hostport string like "127.0.0.1:12000" at the rightmost colon
  14. // into a host part "127.0.0.1" and a port part "12000".
  15. static bool split_host_port(std::string &host, std::string &port,
  16. const std::string &hostport)
  17. {
  18. size_t colon = hostport.find_last_of(':');
  19. if (colon == std::string::npos) {
  20. std::cerr << "Cannot parse \"" << hostport << "\" as host:port\n";
  21. return false;
  22. }
  23. host = hostport.substr(0, colon);
  24. port = hostport.substr(colon+1);
  25. return true;
  26. }
  27. // Convert a single hex character into its value from 0 to 15. Return
  28. // true on success, false if it wasn't a hex character.
  29. static inline bool hextoval(unsigned char &val, char hex)
  30. {
  31. if (hex >= '0' && hex <= '9') {
  32. val = ((unsigned char)hex)-'0';
  33. } else if (hex >= 'a' && hex <= 'f') {
  34. val = ((unsigned char)hex)-'a'+10;
  35. } else if (hex >= 'A' && hex <= 'F') {
  36. val = ((unsigned char)hex)-'A'+10;
  37. } else {
  38. return false;
  39. }
  40. return true;
  41. }
  42. // Convert a 2*len hex character string into a len-byte buffer. Return
  43. // true on success, false on failure.
  44. static bool hextobuf(unsigned char *buf, const char *str, size_t len)
  45. {
  46. if (strlen(str) != 2*len) {
  47. std::cerr << "Hex string was not the expected size\n";
  48. return false;
  49. }
  50. for (size_t i=0;i<len;++i) {
  51. unsigned char hi, lo;
  52. if (!hextoval(hi, str[2*i]) || !hextoval(lo, str[2*i+1])) {
  53. std::cerr << "Cannot parse string as hex\n";
  54. return false;
  55. }
  56. buf[i] = (unsigned char)((hi << 4) + lo);
  57. }
  58. return true;
  59. }
  60. void displayMessage(unsigned char *msg, uint16_t msg_size) {
  61. clientid_t sid, rid;
  62. unsigned char *ptr = msg;
  63. sid = *((clientid_t*) ptr);
  64. ptr+=sizeof(sid);
  65. rid = *((clientid_t*) ptr);
  66. printf("Sender ID: %d, Receiver ID: %d, Token: N/A\n", sid, rid );
  67. printf("Message: ");
  68. for(int j = 0; j<msg_size - sizeof(sid)*2; j++) {
  69. printf("%x", (*ptr));
  70. ptr++;
  71. }
  72. printf("\n");
  73. }
  74. void displayPtMessageBundle(unsigned char *bundle, uint16_t priv_out, uint16_t msg_size) {
  75. unsigned char *ptr = bundle;
  76. uint64_t header = *((uint64_t*) ptr);
  77. ptr+=sizeof(uint64_t);
  78. for(int i=0; i<priv_out; i++) {
  79. displayMessage(ptr, msg_size);
  80. printf("\n");
  81. ptr+=msg_size;
  82. }
  83. printf("\n");
  84. }
  85. void displayEncMessageBundle(unsigned char *bundle, uint16_t priv_out, uint16_t msg_size) {
  86. unsigned char *ptr = bundle;
  87. uint64_t header = *((uint64_t*) ptr);
  88. ptr+=sizeof(uint64_t);
  89. printf("IV: ");
  90. for(int i=0; i<SGX_AESGCM_IV_SIZE; i++) {
  91. printf("%x", ptr[i]);
  92. }
  93. printf("\n");
  94. ptr+= SGX_AESGCM_IV_SIZE;
  95. for(int i=0; i<priv_out; i++) {
  96. displayMessage(ptr, msg_size);
  97. ptr+=msg_size;
  98. }
  99. printf("MAC: ");
  100. for(int i=0; i<SGX_AESGCM_MAC_SIZE; i++) {
  101. printf("%x", ptr[i]);
  102. }
  103. printf("\n");
  104. }
  105. #define HEADER_SIZE 8
  106. static inline uint32_t encMsgBundleSize(uint16_t priv_out, uint16_t msg_size) {
  107. return(HEADER_SIZE + SGX_AESGCM_IV_SIZE + (priv_out * msg_size) + SGX_AESGCM_MAC_SIZE);
  108. }
  109. static inline uint32_t ptMsgBundleSize(uint16_t priv_out, uint16_t msg_size) {
  110. return(HEADER_SIZE + (priv_out * msg_size));
  111. }
  112. bool config_parse(Config &config, const std::string configstr,
  113. std::vector<NodeConfig> &ingestion_nodes,
  114. std::vector<NodeConfig> &storage_nodes)
  115. {
  116. bool found_params = false;
  117. bool ret = true;
  118. std::istringstream configstream(configstr);
  119. boost::property_tree::ptree conftree;
  120. read_json(configstream, conftree);
  121. for (auto & entry : conftree) {
  122. if (!entry.first.compare("params")) {
  123. for (auto & pentry : entry.second) {
  124. if (!pentry.first.compare("msg_size")) {
  125. config.msg_size = pentry.second.get_value<uint16_t>();
  126. } else if (!pentry.first.compare("user_count")) {
  127. config.user_count = pentry.second.get_value<uint32_t>();
  128. } else if (!pentry.first.compare("priv_out")) {
  129. config.m_priv_out = pentry.second.get_value<uint8_t>();
  130. } else if (!pentry.first.compare("priv_in")) {
  131. config.m_priv_in = pentry.second.get_value<uint8_t>();
  132. } else if (!pentry.first.compare("pub_out")) {
  133. config.m_pub_out = pentry.second.get_value<uint8_t>();
  134. } else if (!pentry.first.compare("pub_in")) {
  135. config.m_pub_in = pentry.second.get_value<uint8_t>();
  136. // Currently hardcoding an AES key for client -> server communication
  137. } else if (!pentry.first.compare("master_secret")) {
  138. std::string hex_key = pentry.second.data();
  139. memcpy(config.master_secret, hex_key.c_str(), SGX_AESGCM_KEY_SIZE);
  140. } else {
  141. std::cerr << "Unknown field in params: " <<
  142. pentry.first << "\n";
  143. ret = false;
  144. }
  145. }
  146. found_params = true;
  147. } else if (!entry.first.compare("nodes")) {
  148. for (auto & node : entry.second) {
  149. NodeConfig nc;
  150. // All nodes need to be assigned their role in manifest.yaml
  151. nc.roles = 0;
  152. for (auto & nentry : node.second) {
  153. if (!nentry.first.compare("name")) {
  154. nc.name = nentry.second.get_value<std::string>();
  155. } else if (!nentry.first.compare("pubkey")) {
  156. ret &= hextobuf((unsigned char *)&nc.pubkey,
  157. nentry.second.get_value<std::string>().c_str(),
  158. sizeof(nc.pubkey));
  159. } else if (!nentry.first.compare("weight")) {
  160. nc.weight = nentry.second.get_value<std::uint8_t>();
  161. } else if (!nentry.first.compare("listen")) {
  162. ret &= split_host_port(nc.listenhost, nc.listenport,
  163. nentry.second.get_value<std::string>());
  164. } else if (!nentry.first.compare("clisten")) {
  165. ret &= split_host_port(nc.clistenhost, nc.clistenport,
  166. nentry.second.get_value<std::string>());
  167. } else if (!nentry.first.compare("roles")) {
  168. nc.roles = nentry.second.get_value<std::uint8_t>();
  169. } else {
  170. std::cerr << "Unknown field in host config: " <<
  171. nentry.first << "\n";
  172. ret = false;
  173. }
  174. }
  175. if(nc.roles == ROLE_INGESTION) {
  176. ingestion_nodes.push_back(std::move(nc));
  177. } else if(nc.roles == ROLE_STORAGE) {
  178. storage_nodes.push_back(std::move(nc));
  179. }
  180. }
  181. } else {
  182. std::cerr << "Unknown key in config: " <<
  183. entry.first << "\n";
  184. ret = false;
  185. }
  186. }
  187. if (!found_params) {
  188. std::cerr << "Could not find params in config\n";
  189. ret = false;
  190. }
  191. return ret;
  192. }
  193. static void usage(const char *argv0)
  194. {
  195. fprintf(stderr, "%s [-t nthreads] < config.json\n",
  196. argv0);
  197. exit(1);
  198. }
  199. /*
  200. Generate EMK (Encryption master Secret Key) and TMK (Token master Secret Key)
  201. */
  202. int generateMasterKeys(sgx_aes_gcm_128bit_key_t master_secret,
  203. aes_key &EMK, aes_key &TMK )
  204. {
  205. unsigned char zeroes[SGX_AESGCM_KEY_SIZE];
  206. unsigned char iv[SGX_AESGCM_IV_SIZE];
  207. unsigned char mac[SGX_AESGCM_MAC_SIZE];
  208. memset(iv, 0, SGX_AESGCM_IV_SIZE);
  209. memset(zeroes, 0, SGX_AESGCM_KEY_SIZE);
  210. memcpy(iv, "Encryption", sizeof("Encryption"));
  211. if (sizeof(zeroes) != gcm_encrypt(zeroes, SGX_AESGCM_KEY_SIZE, NULL, 0,
  212. master_secret, iv, SGX_AESGCM_IV_SIZE, EMK, mac)) {
  213. printf("Client: generateMasterKeys FAIL\n");
  214. return -1;
  215. }
  216. printf("\n\nEncryption Master Key: ");
  217. for(int i=0;i<SGX_AESGCM_KEY_SIZE;i++) {
  218. printf("%x", EMK[i]);
  219. }
  220. printf("\n");
  221. memset(iv, 0, SGX_AESGCM_IV_SIZE);
  222. memcpy(iv, "Token", sizeof("Token"));
  223. if (sizeof(zeroes) != gcm_encrypt(zeroes, SGX_AESGCM_KEY_SIZE, NULL, 0,
  224. master_secret, iv, SGX_AESGCM_IV_SIZE, TMK, mac)) {
  225. printf("generateMasterKeys failed\n");
  226. return -1;
  227. }
  228. printf("Token Master Key: ");
  229. for(int i=0;i<SGX_AESGCM_KEY_SIZE;i++) {
  230. printf("%x", TMK[i]);
  231. }
  232. printf("\n\n");
  233. return 1;
  234. }
  235. /*
  236. Takes the client_number, the master aes_key for generating client encryption keys,
  237. and the client aes_key to be generated.
  238. */
  239. int generateClientEncryptionKey(clientid_t client_number, aes_key &EMK, aes_key &client_key)
  240. {
  241. unsigned char zeroes[SGX_AESGCM_KEY_SIZE];
  242. unsigned char iv[SGX_AESGCM_IV_SIZE];
  243. unsigned char tag[SGX_AESGCM_MAC_SIZE];
  244. memset(iv, 0, SGX_AESGCM_IV_SIZE);
  245. memset(zeroes, 0, SGX_AESGCM_KEY_SIZE);
  246. memset(tag, 0, SGX_AESGCM_KEY_SIZE);
  247. memcpy(iv, &client_number, sizeof(client_number));
  248. /*
  249. printf("Client Key: (before Gen) ");
  250. for(int i=0;i<SGX_AESGCM_KEY_SIZE;i++) {
  251. printf("%x", client_key[i]);
  252. }
  253. printf("\n");
  254. */
  255. if (sizeof(zeroes) != gcm_encrypt(zeroes, SGX_AESGCM_KEY_SIZE, NULL, 0, EMK,
  256. iv, SGX_AESGCM_IV_SIZE, client_key, tag)) {
  257. printf("generateClientEncryptionKey failed\n");
  258. return -1;
  259. }
  260. /*
  261. printf("Client Key: (after Gen) ");
  262. for(int i=0;i<SGX_AESGCM_KEY_SIZE;i++) {
  263. printf("%x", client_key[i]);
  264. }
  265. printf("\n");
  266. */
  267. return 1;
  268. }
  269. void Client::initializeSocket(boost::asio::io_context &ioc,
  270. NodeConfig &ing_server) {
  271. boost::system::error_code err;
  272. boost::asio::ip::tcp::resolver resolver(ioc);
  273. while(1) {
  274. #ifdef VERBOSE_NET
  275. std::cerr << "Connecting to " << ing_server.name << "...\n";
  276. std::cout << ing_server.clistenhost << ":" << ing_server.clistenport;
  277. #endif
  278. // ingestion_sock needs io_context
  279. ingestion_sock = new boost::asio::ip::tcp::socket(ioc);
  280. boost::asio::connect(*ingestion_sock,
  281. resolver.resolve(ing_server.clistenhost,
  282. ing_server.clistenport), err);
  283. if (!err) break;
  284. std::cerr << "Connection to " << ing_server.name <<
  285. " refused, will retry.\n";
  286. sleep(1);
  287. }
  288. /*
  289. // Test write 7 to the socket
  290. nodenum_t node_num = 7;
  291. boost::asio::write(*ingestion_sock,
  292. boost::asio::buffer(&node_num, sizeof(node_num)));
  293. */
  294. }
  295. /*
  296. Populates the buffer pt_msgbundle with a valid message pt_msgbundle.
  297. Assumes that it is supplied with a pt_msgbundle buffer of the correct length
  298. Correct length for pt_msgbundle = 8 + (priv_out)*(msg_size) + 16 bytes
  299. */
  300. void Client::generateMessageBundle(uint8_t priv_out, uint32_t msg_size,
  301. unsigned char *pt_msgbundle)
  302. {
  303. unsigned char *ptr = pt_msgbundle;
  304. uint64_t header = (id << 8) + CLIENT_MESSAGE_BUNDLE;
  305. // Setup header
  306. memcpy(ptr, (uint8_t*) &header, sizeof(header));
  307. ptr+=sizeof(header);
  308. // Setup message pt_msgbundle
  309. for(uint32_t i = 0; i < priv_out; i++) {
  310. memcpy(ptr, &id, sizeof(id));
  311. ptr+=(sizeof(id));
  312. memcpy(ptr, &i, sizeof(i));
  313. ptr+=(sizeof(i));
  314. uint32_t remaining_message_size = msg_size - (sizeof(id)*2);
  315. memset(ptr, 0, remaining_message_size);
  316. ptr+=(remaining_message_size);
  317. }
  318. }
  319. bool Client::encryptMessageBundle(uint32_t enc_bundle_size, unsigned char *pt_msgbundle,
  320. unsigned char *enc_msgbundle)
  321. {
  322. // Copy the header
  323. memcpy(enc_msgbundle, pt_msgbundle, HEADER_SIZE);
  324. // Encrypt the rest of the pt_msgbundle
  325. unsigned char *pt_msgbundle_start = pt_msgbundle + HEADER_SIZE;
  326. unsigned char *enc_msgbundle_start = enc_msgbundle + HEADER_SIZE + SGX_AESGCM_IV_SIZE;
  327. unsigned char *enc_tag = enc_msgbundle + enc_bundle_size - SGX_AESGCM_MAC_SIZE;
  328. size_t bytes_to_encrypt = enc_bundle_size - SGX_AESGCM_MAC_SIZE - HEADER_SIZE - SGX_AESGCM_IV_SIZE;
  329. if (bytes_to_encrypt != gcm_encrypt(pt_msgbundle_start, bytes_to_encrypt,
  330. NULL, 0, key, iv, SGX_AESGCM_IV_SIZE, enc_msgbundle_start, enc_tag)) {
  331. printf("Client: encryptMessageBundle FAIL\n");
  332. return 0;
  333. }
  334. // Copy the IV into the bundle
  335. unsigned char *enc_msgbundle_iv = enc_msgbundle + HEADER_SIZE;
  336. memcpy(enc_msgbundle_iv, iv, SGX_AESGCM_IV_SIZE);
  337. // Update IV
  338. uint64_t *iv_ctr = (uint64_t*) iv;
  339. (*iv_ctr)+=1;
  340. return 1;
  341. }
  342. /*
  343. Assumes pt_msgbundle is a buffer of size messageBundleSize(priv_out, msg_size)
  344. */
  345. void Client::sendMessageBundle(uint16_t priv_out, uint16_t msg_size,
  346. unsigned char *pt_msgbundle, unsigned char *enc_msgbundle)
  347. {
  348. uint32_t enc_bundle_size = encMsgBundleSize(priv_out, msg_size);
  349. generateMessageBundle(priv_out, msg_size, pt_msgbundle);
  350. encryptMessageBundle(enc_bundle_size, pt_msgbundle, enc_msgbundle);
  351. //displayPtMessageBundle(pt_msgbundle, priv_out, msg_size);
  352. //displayEncMessageBundle(enc_msgbundle, priv_out, msg_size);
  353. boost::asio::write(*ingestion_sock,
  354. boost::asio::buffer(enc_msgbundle, enc_bundle_size));
  355. }
  356. /*
  357. Spin config.user_client actual clients. Each client:
  358. 1) Retrieve messages and tokens from their storage server
  359. 2) Send all their messages to the ingestion server
  360. 3) Wait for a predetermined EPOCH_DURATION time
  361. 4) Repeat from 1)
  362. */
  363. int main(int argc, char **argv)
  364. {
  365. // Unbuffer stdout
  366. setbuf(stdout, NULL);
  367. uint16_t nthreads = 1;
  368. const char *progname = argv[0];
  369. std::vector<NodeConfig> ingestion_nodes, storage_nodes;
  370. ++argv;
  371. // Parse options
  372. while (*argv && (*argv)[0] == '-') {
  373. if (!strcmp(*argv, "-t")) {
  374. if (argv[1] == NULL) {
  375. usage(progname);
  376. }
  377. nthreads = uint16_t(atoi(argv[1]));
  378. argv += 2;
  379. } else {
  380. usage(progname);
  381. }
  382. }
  383. printf("nthreads = %d\n", nthreads);
  384. // Read the config.json from the first line of stdin. We have to do
  385. // this before outputting anything to avoid potential deadlock with
  386. // the launch program.
  387. std::string configstr;
  388. std::getline(std::cin, configstr);
  389. Config config;
  390. aes_key EMK, TMK, client_key;
  391. boost::asio::io_context io_context;
  392. boost::asio::ip::tcp::resolver resolver(io_context);
  393. if (!config_parse(config, configstr, ingestion_nodes, storage_nodes)) {
  394. exit(1);
  395. }
  396. Client *clients = new Client[config.user_count];
  397. printf("Number of ingestion_nodes = %ld, Number of storage_node = %ld\n",
  398. ingestion_nodes.size(), storage_nodes.size());
  399. generateMasterKeys(config.master_secret, EMK, TMK);
  400. uint32_t num_clients_total = config.user_count;
  401. uint16_t num_ing_nodes = ingestion_nodes.size();
  402. uint32_t clients_per_ing = CEILDIV(num_clients_total, num_ing_nodes);
  403. uint16_t ing_with_additional = num_clients_total % num_ing_nodes;
  404. uint16_t priv_out = config.m_priv_out;
  405. uint16_t msg_size = config.msg_size;
  406. uint32_t pt_bundle_size = ptMsgBundleSize(priv_out, msg_size);
  407. uint32_t enc_bundle_size = encMsgBundleSize(priv_out, msg_size);
  408. unsigned char *pt_msgbundle = (unsigned char*) malloc (pt_bundle_size);
  409. unsigned char *enc_msgbundle = (unsigned char*) malloc (enc_bundle_size);
  410. uint64_t epoch = 1;
  411. while(epoch<2) {
  412. for(uint32_t i=0; i<num_clients_total; i++) {
  413. if(epoch==1) {
  414. uint16_t ing_node_this_client = i/clients_per_ing;
  415. if(ing_node_this_client > ing_with_additional && ing_with_additional!=0) {
  416. uint16_t leftover = num_clients_total - (ing_with_additional * clients_per_ing);
  417. ing_node_this_client = ing_with_additional + (leftover / (clients_per_ing-1));
  418. }
  419. int ret = generateClientEncryptionKey(i, EMK, client_key);
  420. clients[i].initClient(i, client_key);
  421. clients[i].initializeSocket(io_context, ingestion_nodes[ing_node_this_client]);
  422. //clients[i].sendAuthMessage();
  423. /*
  424. // Test that the keys generated match those generated within
  425. // enclave config
  426. unsigned char *ckey;
  427. ckey = clients[i].getKey();
  428. printf("Client %d, id = %d, key: ", i, clients[i].getid());
  429. for(int j=0;j<SGX_AESGCM_KEY_SIZE;j++) {
  430. printf("%x", ckey[j]);
  431. }
  432. printf("\n\n");
  433. */
  434. }
  435. clients[i].sendMessageBundle(priv_out, msg_size, pt_msgbundle, enc_msgbundle);
  436. }
  437. epoch++;
  438. sleep(1);
  439. }
  440. free(pt_msgbundle);
  441. delete [] clients;
  442. }