clients.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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. bool config_parse(Config &config, const std::string configstr,
  61. std::vector<NodeConfig> &ingestion_nodes,
  62. std::vector<NodeConfig> &storage_nodes)
  63. {
  64. bool found_params = false;
  65. bool ret = true;
  66. std::istringstream configstream(configstr);
  67. boost::property_tree::ptree conftree;
  68. read_json(configstream, conftree);
  69. for (auto & entry : conftree) {
  70. if (!entry.first.compare("params")) {
  71. for (auto & pentry : entry.second) {
  72. if (!pentry.first.compare("msg_size")) {
  73. config.msg_size = pentry.second.get_value<uint16_t>();
  74. } else if (!pentry.first.compare("user_count")) {
  75. config.user_count = pentry.second.get_value<uint32_t>();
  76. } else if (!pentry.first.compare("priv_out")) {
  77. config.m_priv_out = pentry.second.get_value<uint8_t>();
  78. } else if (!pentry.first.compare("priv_in")) {
  79. config.m_priv_in = pentry.second.get_value<uint8_t>();
  80. } else if (!pentry.first.compare("pub_out")) {
  81. config.m_pub_out = pentry.second.get_value<uint8_t>();
  82. } else if (!pentry.first.compare("pub_in")) {
  83. config.m_pub_in = pentry.second.get_value<uint8_t>();
  84. // Currently hardcoding an AES key for client -> server communication
  85. } else if (!pentry.first.compare("master_secret")) {
  86. std::string hex_key = pentry.second.data();
  87. memcpy(config.master_secret, hex_key.c_str(), SGX_AESGCM_KEY_SIZE);
  88. } else {
  89. std::cerr << "Unknown field in params: " <<
  90. pentry.first << "\n";
  91. ret = false;
  92. }
  93. }
  94. found_params = true;
  95. } else if (!entry.first.compare("nodes")) {
  96. for (auto & node : entry.second) {
  97. NodeConfig nc;
  98. // All nodes need to be assigned their role in manifest.yaml
  99. nc.roles = 0;
  100. for (auto & nentry : node.second) {
  101. if (!nentry.first.compare("name")) {
  102. nc.name = nentry.second.get_value<std::string>();
  103. } else if (!nentry.first.compare("pubkey")) {
  104. ret &= hextobuf((unsigned char *)&nc.pubkey,
  105. nentry.second.get_value<std::string>().c_str(),
  106. sizeof(nc.pubkey));
  107. } else if (!nentry.first.compare("weight")) {
  108. nc.weight = nentry.second.get_value<std::uint8_t>();
  109. } else if (!nentry.first.compare("listen")) {
  110. ret &= split_host_port(nc.listenhost, nc.listenport,
  111. nentry.second.get_value<std::string>());
  112. } else if (!nentry.first.compare("clisten")) {
  113. ret &= split_host_port(nc.clistenhost, nc.clistenport,
  114. nentry.second.get_value<std::string>());
  115. } else if (!nentry.first.compare("roles")) {
  116. nc.roles = nentry.second.get_value<std::uint8_t>();
  117. } else {
  118. std::cerr << "Unknown field in host config: " <<
  119. nentry.first << "\n";
  120. ret = false;
  121. }
  122. }
  123. if(nc.roles == ROLE_INGESTION) {
  124. ingestion_nodes.push_back(std::move(nc));
  125. } else if(nc.roles == ROLE_STORAGE) {
  126. storage_nodes.push_back(std::move(nc));
  127. }
  128. }
  129. } else {
  130. std::cerr << "Unknown key in config: " <<
  131. entry.first << "\n";
  132. ret = false;
  133. }
  134. }
  135. if (!found_params) {
  136. std::cerr << "Could not find params in config\n";
  137. ret = false;
  138. }
  139. return ret;
  140. }
  141. static void usage(const char *argv0)
  142. {
  143. fprintf(stderr, "%s [-t nthreads] < config.json\n",
  144. argv0);
  145. exit(1);
  146. }
  147. /*
  148. Generate ESK (Encryption master Secret Key) and TSK (Token master Secret Key)
  149. */
  150. int generateMasterKeys(sgx_aes_gcm_128bit_key_t master_secret,
  151. aes_key &ESK, aes_key &TSK )
  152. {
  153. unsigned char zeroes[SGX_AESGCM_KEY_SIZE];
  154. unsigned char iv[SGX_AESGCM_IV_SIZE];
  155. unsigned char mac[SGX_AESGCM_MAC_SIZE];
  156. memset(iv, 0, SGX_AESGCM_IV_SIZE);
  157. memset(zeroes, 0, SGX_AESGCM_KEY_SIZE);
  158. memcpy(iv, "Encryption", sizeof("Encryption"));
  159. if (sizeof(zeroes) != gcm_encrypt(zeroes, SGX_AESGCM_KEY_SIZE, NULL, 0,
  160. master_secret, iv, SGX_AESGCM_IV_SIZE, ESK, mac)) {
  161. printf("Client: generateMasterKeys FAIL\n");
  162. return -1;
  163. }
  164. printf("Encryption Master Key: ");
  165. for(int i=0;i<SGX_AESGCM_KEY_SIZE;i++) {
  166. printf("%x", ESK[i]);
  167. }
  168. printf("\n\n");
  169. memset(iv, 0, SGX_AESGCM_IV_SIZE);
  170. memcpy(iv, "Token", sizeof("Token"));
  171. if (sizeof(zeroes) != gcm_encrypt(zeroes, SGX_AESGCM_KEY_SIZE, NULL, 0,
  172. master_secret, iv, SGX_AESGCM_IV_SIZE, TSK, mac)) {
  173. printf("generateMasterKeys failed\n");
  174. return -1;
  175. }
  176. printf("Token Master Key: ");
  177. for(int i=0;i<SGX_AESGCM_KEY_SIZE;i++) {
  178. printf("%x", TSK[i]);
  179. }
  180. printf("\n");
  181. return 1;
  182. }
  183. /*
  184. Takes the client_number, the master aes_key for generating client encryption keys,
  185. and the client aes_key to be generated.
  186. */
  187. int generateClientEncryptionKey(clientid_t client_number, aes_key &ESK, aes_key &client_key)
  188. {
  189. unsigned char zeroes[SGX_AESGCM_KEY_SIZE];
  190. unsigned char mac[SGX_AESGCM_MAC_SIZE];
  191. unsigned char iv[SGX_AESGCM_IV_SIZE];
  192. memset(zeroes, 0, SGX_AESGCM_KEY_SIZE);
  193. memset(iv, 0, SGX_AESGCM_IV_SIZE);
  194. memcpy(iv, (unsigned char*) (&client_number), sizeof(client_number));
  195. // GCM-encrypt, using the chunk key as the associated data
  196. if (sizeof(zeroes) != gcm_encrypt(zeroes, SGX_AESGCM_KEY_SIZE, NULL, 0, ESK,
  197. iv, SGX_AESGCM_IV_SIZE, client_key, mac)) {
  198. printf("generateClientEncryptionKey failed\n");
  199. return -1;
  200. }
  201. return 1;
  202. }
  203. int main(int argc, char **argv)
  204. {
  205. // Unbuffer stdout
  206. setbuf(stdout, NULL);
  207. uint16_t nthreads = 1;
  208. const char *progname = argv[0];
  209. std::vector<NodeConfig> ingestion_nodes, storage_nodes;
  210. ++argv;
  211. // Parse options
  212. while (*argv && (*argv)[0] == '-') {
  213. if (!strcmp(*argv, "-t")) {
  214. if (argv[1] == NULL) {
  215. usage(progname);
  216. }
  217. nthreads = uint16_t(atoi(argv[1]));
  218. argv += 2;
  219. } else {
  220. usage(progname);
  221. }
  222. }
  223. // Read the config.json from the first line of stdin. We have to do
  224. // this before outputting anything to avoid potential deadlock with
  225. // the launch program.
  226. std::string configstr;
  227. std::getline(std::cin, configstr);
  228. Config config;
  229. aes_key ESK, TSK;
  230. Client *clients = new Client[config.user_count];
  231. if (!config_parse(config, configstr, ingestion_nodes, storage_nodes)) {
  232. exit(1);
  233. }
  234. printf("Number of ingestion_nodes = %ld, Number of storage_node = %ld\n",
  235. ingestion_nodes.size(), storage_nodes.size());
  236. generateMasterKeys(config.master_secret, ESK, TSK);
  237. uint32_t clients_per_ing = CEILDIV(config.user_count, ingestion_nodes.size());
  238. aes_key client_key;
  239. for(uint32_t i=0; i<config.user_count; i++) {
  240. generateClientEncryptionKey(i, ESK, client_key);
  241. clients[i].setKey(client_key);
  242. printf("Client %d key: ", i);
  243. for(int i=0;i<SGX_AESGCM_KEY_SIZE;i++) {
  244. printf("%x", client_key[i]);
  245. }
  246. printf("\n");
  247. }
  248. /*
  249. // Attempt sending a data packet to one of the ingestion servers
  250. boost::asio::io_context io_context;
  251. boost::system::error_code err;
  252. boost::asio::ip::tcp::socket nodesock(io_context);
  253. boost::asio::ip::tcp::resolver resolver(io_context);
  254. while(1) {
  255. #ifdef VERBOSE_NET
  256. std::cerr << "Connecting to " << ingestion_nodes[0].name << "...\n";
  257. #endif
  258. std::cout << ingestion_nodes[0].clistenhost << ":" << ingestion_nodes[0].clistenport;
  259. boost::asio::connect(nodesock,
  260. resolver.resolve(ingestion_nodes[0].clistenhost,
  261. ingestion_nodes[0].clistenport), err);
  262. if (!err) break;
  263. std::cerr << "Connection to " << ingestion_nodes[0].name <<
  264. " refused, will retry.\n";
  265. sleep(1);
  266. }
  267. nodenum_t node_num = 7;
  268. boost::asio::write(nodesock,
  269. boost::asio::buffer(&node_num, sizeof(node_num)));
  270. */
  271. /*
  272. int randfd = open("/dev/urandom", O_RDONLY);
  273. if (randfd < 0) {
  274. throw std::runtime_error("Cannot open /dev/urandom");
  275. }
  276. unsigned char zeroes[config.msg_size];
  277. unsigned char buffer[config.msg_size+4+12+16];
  278. memset(zeroes, 0, sizeof(zeroes));
  279. // An AES key for constructing and verifying the _plaintext_ of the blocks
  280. // so that we can ensure that the blocks come out unaltered
  281. unsigned char datakey[16];
  282. read(randfd, datakey, 16);
  283. // GCM-encrypt, using the chunk key as the associated data
  284. if (sizeof(zeroes) != gcm_encrypt(zeroes, sizeof(zeroes), buffer, 4, datakey,
  285. buffer+4, 12, buffer+4+12, buffer+4+12+sizeof(zeroes))) {
  286. printf("Inner encryption failed\n");
  287. }
  288. */
  289. /*
  290. Spin config.user_client actual clients. Each client:
  291. 1) Retrieve messages and tokens from their storage server
  292. 2) Send all their messages to the ingestion server
  293. 3) Wait for a predetermined EPOCH_DURATION time
  294. 4) Repeat from 1)
  295. */
  296. delete [] clients;
  297. }