PIROptimizer.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068
  1. /* Copyright (C) 2014 Carlos Aguilar Melchor, Joris Barrier, Marc-Olivier Killijian
  2. * This file is part of XPIR.
  3. *
  4. * XPIR is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * XPIR is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with XPIR. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include "PIROptimizer.hpp"
  18. // Set this to true if the server database is stored in precomputed (e.g. NTT) form
  19. // TODO this should be an option given on the command line
  20. #define INITIAL_PRECOMPUTATION_DONE false
  21. static const unsigned int kPrecision = 5;
  22. const unsigned int PIROptimizer::kalphaBound = 100;
  23. /**
  24. * Class constructor.
  25. * Params :
  26. * - pirClient_ptr client : PIRClient shared_ptr ;
  27. * - int security_bits : security bits wanted ;
  28. * - string server_ip : IPv4 server adresse ;
  29. * - io_service& ios : boost io_service reference, used for boost socket ;
  30. **/
  31. PIROptimizer::PIROptimizer(string server_ip_, int port_, FitnessType fitnessMethod_) :
  32. serv_ip(server_ip_),
  33. server_optimport(port_),
  34. s(ios),
  35. // Number of clients the servers would like to handle in parallel : Fixed for the moment (the server will tell in future versions)
  36. nbc(1),
  37. optimum(fitnessMethod_),
  38. silent(false)
  39. {
  40. // Max latency for the socket when connecting to server
  41. struct timeval tv;
  42. tv.tv_sec = 5;
  43. tv.tv_usec = 0;
  44. setsockopt(s.native(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
  45. setsockopt(s.native(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
  46. }
  47. PIROptimizer::PIROptimizer():
  48. s(ios),
  49. nbc(1),
  50. silent(false)
  51. {}
  52. OptimVars PIROptimizer::optimizeFromServer(FixedVars& partial_fixed_vars, boost::asio::ip::tcp::socket& socket)
  53. {
  54. fixedVars = partial_fixed_vars;
  55. getFixedVarsFromServer(fixedVars, socket);
  56. getNetworkInfos(fixedVars, socket);
  57. optimize(socket, fixedVars, 0);
  58. sendExitCommand(socket);
  59. return global_optimum;
  60. }
  61. void PIROptimizer::optimize(FixedVars& fixed_vars, unsigned int exp_nbr)
  62. {
  63. try
  64. {
  65. s.connect(tcp::endpoint(boost::asio::ip::address::from_string(serv_ip), server_optimport));
  66. // Tell server we are an optimizer not a client
  67. int is_client = 0;
  68. write(s, boost::asio::buffer(&is_client, sizeof(int)));
  69. }
  70. catch (const std::exception& ex)
  71. {
  72. std::cout << "Optimizer: Unable to connect to "<< serv_ip << " port " << server_optimport <<std::endl;
  73. }
  74. optimize(s, fixed_vars, exp_nbr);
  75. }
  76. /**
  77. * Main function of PIROptimizer.
  78. * Loop on all crypto modules to finc possible parameters
  79. * Initialize caches with computing costs
  80. * Iterate on all parameters except aggregation
  81. * Use findAlphaDicho to explore best aggregation values through dichotomy
  82. **/
  83. void PIROptimizer::optimize(boost::asio::ip::tcp::socket& s, FixedVars& fixed_vars, unsigned int exp_nbr)
  84. {
  85. std::regex *cp_rex;
  86. fixedVars = fixed_vars;
  87. // Do we optimize the sum of all the times or we consider the client and the server as pipelines (sum of max) ?
  88. fitnessMethod = fixedVars.fitness;
  89. std::cout << "Optimizer: Using fitness method " << fitnessMethod << " (use --help for code translation)" << std::endl;
  90. set<std::string> crypto_params_desc_set;
  91. vector<HomomorphicCrypto*> crypto_systems_vec;
  92. // Get all the crypto modules
  93. HomomorphicCryptoFactory_internal::getAllCryptoSystems(crypto_systems_vec);
  94. if(fixedVars.manual_crypto_params == "")
  95. {
  96. noCryptoSystemOptim(exp_nbr);
  97. }
  98. else
  99. {
  100. std::cout << "Optimizer: Crypto parameters manually forced to " << fixedVars.manual_crypto_params << std::endl;
  101. // Get a regular expression from the requested params
  102. cp_rex = new std::regex(fixedVars.manual_crypto_params);
  103. // Only take into account NoCryptography as a possibility if requested
  104. if(std::regex_match("NoCryptography", *cp_rex))
  105. {
  106. noCryptoSystemOptim(exp_nbr);
  107. }
  108. }
  109. // else
  110. // {
  111. // std::vector<std::string> fields;
  112. // boost::algorithm::split(fields, fixedVars.manual_crypto_params, boost::algorithm::is_any_of(":"));
  113. // HomomorphicCrypto* h;
  114. //
  115. // h = HomomorphicCryptoFactory_internal::getCrypto(fields[0]);
  116. // crypto_systems_vec.push_back(h);
  117. // }
  118. // }
  119. // Iterate on all crypto modules
  120. for (auto crypto_system_ptr : crypto_systems_vec)
  121. {
  122. crypto = crypto_system_ptr;
  123. //Get i-th crypto module's public parameter
  124. currentPublicParams = &crypto->getPublicParameters();
  125. // Get all the proposed parameter sets for the requested security on this crypto module
  126. crypto->getCryptoParams(fixedVars.k, crypto_params_desc_set);
  127. // If special parameters requested, remove all those that don't match the regexp
  128. if(fixedVars.manual_crypto_params != "")
  129. {
  130. const set<std::string> crypto_params_desc_set_copy=crypto_params_desc_set;
  131. for (auto cp_ptr : crypto_params_desc_set_copy)
  132. {
  133. if (!std::regex_match(cp_ptr, *cp_rex)) crypto_params_desc_set.erase(cp_ptr);
  134. //HomomorphicCrypto* h;
  135. //h = HomomorphicCryptoFactory_internal::getCrypto(fields[0]);
  136. //crypto_systems_vec.push_back(h);
  137. }
  138. if (crypto_params_desc_set.size() == 0) continue;
  139. //crypto_params_desc_set.insert(fixedVars.manual_crypto_params);
  140. }
  141. // Add to cost dictionnaries (abs_cache and precompute_cache) estimates from server
  142. // or pre-established values if no server available
  143. getAbsAndPreCachesFromServer(s, crypto);
  144. // Add to cost dictionnaries (encryptcache, decryptcache) cost estimates (pot. precomputed)
  145. getPreComputedData(crypto);
  146. // Iterate on all the parameters for this crypto module
  147. for (std::string crypto_params_desc : crypto_params_desc_set)
  148. {
  149. // Set the current public parameters to the parameter set values
  150. currentPublicParams->computeNewParameters(crypto_params_desc);
  151. // Iterate on the dimensions
  152. for (unsigned int d = fixedVars.dMin ; d <= fixedVars.dMax ; d++)
  153. {
  154. // Define a OptimVars object (with the variables that we can choose for optimization)
  155. OptimVars vars(fixedVars.fitness, fixedVars);
  156. vars.crypto_params = crypto_params_desc;
  157. // Get best alpha for this iteration given the fitness method and set the variables in vars accordingly
  158. // - First do a test with no aggreggation
  159. findAlpha(d, 1, 1, vars, exp_nbr);
  160. // - Then search with dichotomy
  161. findAlphaDicho(0, getMaxAlpha(), d, exp_nbr, vars);
  162. }
  163. }
  164. // Show and write the optimized values.
  165. processResults(exp_nbr);
  166. // Save best optimum inter cryptosystems
  167. std::cout << "Optimizer: Comparing (" << optimum.crypto_params << ", " << optimum.getValue() << ") with (" << global_optimum.crypto_params << ", " << global_optimum.getValue() << ")" << std::endl;
  168. if(optimum < global_optimum)
  169. {
  170. global_optimum = optimum;
  171. }
  172. std::cout << "Optimizer: Winner is (" << global_optimum.crypto_params << ", " << global_optimum.getValue() << ")" << std::endl;
  173. // Clean
  174. optimum.reset();
  175. crypto_params_desc_set.clear();
  176. delete crypto;
  177. }
  178. disconnect();
  179. // If nothing was found say it and exit
  180. if (global_optimum.crypto_params == "No crypto params")
  181. {
  182. std::cout << "Optimizer: No valid crypto parameters matching " << fixedVars.manual_crypto_params << " found, exiting ..." << std::endl;
  183. exit(0);
  184. }
  185. }
  186. /**
  187. * Set optimum with values giving by a trivial PIR. (i.e download the entiere database)
  188. **/
  189. void PIROptimizer::noCryptoSystemOptim(unsigned int exp_nbr)
  190. {
  191. optimum.crypto_params = "NoCryptography";
  192. optimum.setFixedVars(fixedVars);
  193. optimum.setGenQ(0);
  194. optimum.setSendQ(0);
  195. optimum.setGenR(0);
  196. optimum.setSendR((fixedVars.l * fixedVars.n) / fixedVars.Tdoc);
  197. optimum.setDecR(0);
  198. optimum.setDim(1);
  199. optimum.setAlpha(fixedVars.n);
  200. if (silent == false) showBestResults(exp_nbr);
  201. // Save this as the best result inter cryptosystems
  202. global_optimum = optimum;
  203. optimum.reset();
  204. }
  205. /**
  206. * Find Iteratively the best alpha and set the optimal variables in var
  207. **/
  208. void PIROptimizer::findAlpha(unsigned int d, OptimVars& vars, unsigned int exp_nbr)
  209. {
  210. // alphaMax == 0 is a convention to note that no limit should be done in aggregation
  211. unsigned int alpha_max = (fixedVars.alphaMax == 0) ? fixedVars.n : fixedVars.alphaMax;
  212. //Really get best alpha between 0 and alpha_max
  213. findAlpha(d, 0, alpha_max, vars, exp_nbr);
  214. }
  215. void PIROptimizer::findAlpha(unsigned int d, unsigned int alpha_min, unsigned int alpha_max, OptimVars& vars, unsigned int exp_nbr)
  216. {
  217. // Number of bits that can be absorbed by a ciphertext for our parameters
  218. long abs_bit;
  219. // If aggregation is done, try to use all plaintext space
  220. unsigned int minimum_reasonable_alpha = getMinAlpha();
  221. // Iterate on possible aggregation values
  222. for (unsigned int current_alpha = alpha_min ; current_alpha <= alpha_max ; current_alpha+=minimum_reasonable_alpha)
  223. {
  224. // In first iteration current_alpha=0 is treated as 1 (i.e. no aggregation)
  225. computeOptimVars(current_alpha, d, vars);
  226. // If no bits can be absorbed for this choice ignore it
  227. abs_bit = crypto->getPublicParameters().getAbsorptionBitsize();
  228. if(abs_bit <= 0)
  229. {
  230. cout << "PIROptimizer: Unusable cryptoparams, ignoring them" << endl;
  231. break;
  232. }
  233. // Write test result to output file
  234. OptimService::writeTestCurrentResult(1, alpha_max, current_alpha, 1, alpha_max, d, exp_nbr, vars);
  235. }
  236. }
  237. void PIROptimizer::findAlphaDicho(unsigned int inf, unsigned int sup, unsigned int d, unsigned int exp_nbr, OptimVars& vars)
  238. {
  239. unsigned int min_alpha = getMinAlpha();
  240. //When the space to explore is relatively little we use an iterative method to steer clear of local optima.
  241. if ((sup - inf) <= 100)
  242. {
  243. unsigned int max_alpha_bound = 0;
  244. if (fixedVars.n < (sup * min_alpha))
  245. max_alpha_bound = fixedVars.n;
  246. else
  247. max_alpha_bound = min(fixedVars.alphaMax, max(kalphaBound, sup * min_alpha));
  248. findAlpha(d, inf * min_alpha, max_alpha_bound, vars, exp_nbr);
  249. return;
  250. }
  251. //Next value to test.
  252. unsigned int alpha_bound = inf + (sup - inf) / 2.0;
  253. //Return the state of the slope (up or down).
  254. int val = slop(alpha_bound, inf, sup, d, vars, exp_nbr);
  255. //Write test restult to output file.
  256. OptimService::writeTestCurrentResult(1, sup*getMinAlpha(), alpha_bound*getMinAlpha(), inf*getMinAlpha(), sup*getMinAlpha(), d, exp_nbr, vars);
  257. if(val == -1) {
  258. findAlphaDicho(alpha_bound, sup, d, exp_nbr, vars);
  259. return;
  260. }
  261. findAlphaDicho(inf, alpha_bound, d, exp_nbr, vars);
  262. }
  263. int PIROptimizer::slop(unsigned int alphaMul, unsigned int inf, unsigned int sup, unsigned int d, OptimVars& vars, unsigned int exp_nbr)
  264. {
  265. // Try ten uniformly spaced values ahead to try to find a better result
  266. unsigned int deltaright = (sup-1-alphaMul)/10;
  267. unsigned int deltaleft = (alphaMul - inf-1)/10;
  268. unsigned int minalpha = getMinAlpha();
  269. double vright, vleft, vtmp;
  270. // Ten increasing tests take the best
  271. // ten decreasings tests take the best
  272. // compare the two bests to determine the slope
  273. // each side must have alphaMul+-1 at least
  274. vright = computeOptimVars((alphaMul + 1) * minalpha, d, vars);
  275. for (unsigned int i = 1 ; i <= 10 ; i++)
  276. {
  277. vtmp = computeOptimVars((alphaMul + 1 + i * deltaright) * minalpha, d, vars);
  278. if (vtmp <= vright) vright = vtmp;
  279. }
  280. vleft = computeOptimVars((alphaMul - 1) * minalpha, d, vars);
  281. for (unsigned int i = 1 ; i <= 10 ; i++)
  282. {
  283. vtmp = computeOptimVars((alphaMul - 1 - i * deltaleft) * minalpha, d, vars);
  284. if (vtmp <= vleft) vleft = vtmp;
  285. }
  286. return (vright < vleft - 10e-8) ? -1 : 1 ;
  287. }
  288. /**
  289. * Compute dimension sizes for a d-dimensional hypercube of n elements.
  290. * Params:
  291. * - unsigned int n: the number of elements
  292. * - unsigned int alpha: the aggregation value
  293. * - unsigned int d: the dimension
  294. * - unsigned int *dn: computed dimension sizes (output)
  295. **/
  296. void PIROptimizer::getDimSize(unsigned int n, unsigned int alpha, unsigned int d, unsigned int *dn)
  297. {
  298. unsigned int prod = 1, j = 0;
  299. // Elements in the database after the aggregation
  300. unsigned int new_n = ceil(static_cast<double>(n)/static_cast<double>(alpha)); //PAtch tres sale à reprendre
  301. // Dimension sizes. Product must be > n/alpha
  302. unsigned int factors[d];
  303. // Lower bound on the dimensions sizes needed. Correct only if n/alpha is a d-power.
  304. for (unsigned int i = 0 ; i < d ; i++) factors[i] = floor(pow(new_n,1./d));
  305. // If n/alpha is not a d-power
  306. if (static_cast<double>(factors[0]) != pow(new_n, static_cast<double>(1.0 / static_cast<double>(d))) )
  307. {
  308. // Increment each factor by one until the product of the dimension sizes is above n/alpha
  309. while (prod < new_n && j < d)
  310. {
  311. prod = 1;
  312. factors[j++]++;
  313. for (unsigned int i = 0 ; i < d ; i++)
  314. prod *= factors[i];
  315. }
  316. }
  317. // Copy the result to the output
  318. memcpy(dn, factors, sizeof(factors));
  319. }
  320. /**
  321. * Compute time needed to send the request.
  322. * Params:
  323. * - double Tupc : client upload throughput
  324. * - double Tdos : server download throughput
  325. * - unsigned int nbc : number of clients that share server throughput (currently always 1);
  326. * - unsigned int d : number of dimensions used for recursion
  327. *
  328. * Return:
  329. * - double : time in seconds to send the PIR query.
  330. **/
  331. double PIROptimizer::getSendCost(double Tupc, double Tdos, unsigned int nbc, unsigned int d, unsigned int* dn)
  332. {
  333. // Available throughput for the transfer
  334. double min_throughput = min(Tupc, Tdos/nbc );
  335. // Time needed to send the query
  336. double SenQ = 0.0;
  337. // Sum the times needed for the subqueries of each dimension in the recursion
  338. for (unsigned int i = 0 ; i < d ; i++)
  339. {
  340. // For a given dimension time = size / throughput and size = (nb_elements * size_of_an_element)
  341. SenQ += (dn[i] * Tcq(i)) / min_throughput;
  342. }
  343. //std::cout << "dn[0]="<<dn[0]<<" d=" << d << " Tcq(0)=" << Tcq(0) << " Tupc=" << Tupc << " Tdos=" << Tdos << " SenQ=" << SenQ << std::endl;
  344. return SenQ;
  345. }
  346. /**
  347. * Compute reply generation cost.
  348. * Params:
  349. * - unsigned int d : number of dimension used for recursion
  350. * - unsigned int Tmi : plaintext size on the i-th level of recursion
  351. *
  352. * Return:
  353. * - double : time in second to generate a reply.
  354. **/
  355. double PIROptimizer::getReplyGenCost(unsigned int alpha, unsigned int d, unsigned int *dn)
  356. {
  357. long double prod, GenR = 0;
  358. uint64_t plaintext_nbr_post_ntt, plaintext_nbr_pre_ntt;
  359. bool initialprecomputationdone = INITIAL_PRECOMPUTATION_DONE;
  360. // In order to compute absorption size we must define it first
  361. crypto->setandgetAbsBitPerCiphertext(dn[0]);
  362. for (unsigned int i = 0 ; i < d ; i++)
  363. {
  364. // Compute how many elements has the intermediate database
  365. prod = 1;
  366. for (unsigned int j = i ; j < d ; j++)
  367. {
  368. prod *= dn[j];
  369. }
  370. // Compute how large they are
  371. plaintext_nbr_post_ntt = ceil((double) eltSize(alpha,i) / (double) crypto->getPublicParameters().getAbsorptionBitsize(i));
  372. plaintext_nbr_pre_ntt = ceil((double) eltSize(alpha,i) / (double) crypto->getPublicParameters().getCiphertextBitsize()/2);
  373. // Consider absorption cost
  374. GenR += prod * plaintext_nbr_post_ntt * getAbs1PlaintextTime(crypto);
  375. // And potentially precomputation cost
  376. if (i > 0 || initialprecomputationdone == false)
  377. {
  378. GenR += prod * plaintext_nbr_pre_ntt * getPrecompute1PlaintextTime(crypto);
  379. }
  380. }
  381. return GenR;
  382. }
  383. /**
  384. * Compute the size of a element at dimension "i".
  385. * Params :
  386. * - unsigned int i : dimension ;
  387. * - unsigned int Tmi : encrypted size.
  388. *
  389. * Return :
  390. * - double : the size of an element at dimension "i".
  391. **/
  392. double PIROptimizer::eltSize(unsigned int alpha, unsigned int i)
  393. {
  394. if (i == 0)
  395. {
  396. return static_cast<double>(fixedVars.l * alpha);
  397. }
  398. return ceil( static_cast<double>(eltSize(alpha, i - 1) / Tp(i-1) )) * Tcr(i-1);
  399. }
  400. double PIROptimizer::Tp(unsigned int i)
  401. {
  402. return currentPublicParams->getAbsorptionBitsize(i);
  403. }
  404. /**
  405. * Compute decryption cost.
  406. * Params :
  407. * - unsigned int d : dimension ;
  408. * - unsigned int Tmi : encrypted size.
  409. *
  410. * Return :
  411. * - double : time in seconds to decrypt something with given paramtes.
  412. **/
  413. double PIROptimizer::getDecryptCost(unsigned int alpha, unsigned int d)
  414. {
  415. double DecR = 0;
  416. for (unsigned int i = 0 ; i < d ; i++)
  417. {
  418. // Yet another strange formula
  419. DecR += ceill((eltSize(alpha, i + 1) / Tcr(i))) * getDecCost();
  420. }
  421. return DecR;
  422. }
  423. /**
  424. * Return the size of reply for dimension "i"
  425. **/
  426. inline double PIROptimizer::Tcr(unsigned i)
  427. {
  428. return currentPublicParams->getCiphBitsizeFromRecLvl(i+1);
  429. }
  430. /**
  431. * Return the size of a query for dimension "i"
  432. **/
  433. inline double PIROptimizer::Tcq(unsigned i)
  434. {
  435. return Tcr(i);
  436. }
  437. /**
  438. * Return the time in secondes to decrypt data at dimension "i"
  439. **/
  440. double PIROptimizer::getDecCost(unsigned int d, crypto_ptr crypto)
  441. {
  442. PIRParameters pirParams; //Works for d = 1
  443. pirParams.d = 1;
  444. pirParams.alpha = 1;
  445. unsigned int chunks = 1;
  446. double start, stop, elapsed_time = 0;
  447. // Needed for calls to getAbsorptionBitsize
  448. crypto->setandgetAbsBitPerCiphertext(1); // Set internally best absorption possible
  449. // Use a special function to generate a ciphertext to be sure its decryption cost is average
  450. char* encrypted_data = (char*) crypto->encrypt_perftest();
  451. PIRReplyExtraction_internal replyExt(pirParams,*crypto);
  452. shared_queue<char*> clearChunks("clear_chunks");
  453. do{
  454. chunks *= 2;
  455. for(unsigned int i = 0 ; i < chunks ; i++) //Fill, reply buffer with copy of "encrypted_data".
  456. {
  457. char* encrypted_data_copy = (char*) malloc((crypto->getPublicParameters().getCiphertextBitsize()/8) * sizeof(char));
  458. memcpy(encrypted_data_copy, encrypted_data, crypto->getPublicParameters().getCiphertextBitsize()/8);
  459. replyExt.repliesBuffer.push(encrypted_data_copy);
  460. }
  461. start = omp_get_wtime();
  462. replyExt.extractReply((currentPublicParams->getAbsorptionBitsize()/8) * chunks, &clearChunks); //Do a PIR data extraction.
  463. stop = omp_get_wtime();
  464. while(!clearChunks.empty())
  465. free(clearChunks.pop_front()); //free clear data.
  466. }while((elapsed_time = (stop - start)) < 0.20);
  467. double result = elapsed_time / chunks;
  468. return result;
  469. }
  470. double PIROptimizer::getDecCost()
  471. {
  472. bool shortversion=true;
  473. return decrypt_cache[currentPublicParams->getSerializedParams(shortversion)];
  474. }
  475. /**
  476. * Compute SendR.
  477. * Params :
  478. * - double Tups : server upload time/bit ;
  479. * - double Tdoc : client download time/bit;
  480. * - unsigned int nbc : number of client ;
  481. * - unsigned int Tmi : encrypted size.
  482. * Return :
  483. * - double : Duration in seconds.
  484. **/
  485. double PIROptimizer::getReceiveCost(unsigned int alpha, double Tups, double Tdoc, unsigned int nbc, unsigned int d)
  486. {
  487. // This is a bad clone of eltSize. To be removed.
  488. //double SendR = eltSize(alpha, 0);
  489. //
  490. //for (unsigned int i = 0 ; i < d; i++)
  491. //{
  492. // SendR = ceil(SendR / Tp(i)) * Tcr(i);
  493. //}
  494. //SendR /= min(Tups/nbc, Tdoc);
  495. //
  496. //return SendR;
  497. // eltSize computes how db elements grow with recursion.
  498. // Reply size is what would be the element size for the d-th recursion (rec levels start at 0)
  499. return eltSize(alpha,d)/min(Tups/nbc, Tdoc);
  500. }
  501. /**
  502. * Get absorption time for 1 bit.
  503. * Param :
  504. * - unsigned int d : dimension.
  505. **/
  506. void PIROptimizer::getAbsAndPreCachesFromServer(boost::asio::ip::tcp::socket& s, crypto_ptr crypto_system)
  507. {
  508. size_t cmd = ABS;
  509. try
  510. {
  511. write(s, boost::asio::buffer(&cmd, sizeof(size_t)));
  512. std::string crypto_name = crypto_system->toString();
  513. unsigned int crypto_name_size = crypto_name.size();
  514. std::cout << "Optimizer: Requesting absorption and precomputation costs file for " << crypto_name << std::endl;
  515. write(s, boost::asio::buffer(&crypto_name_size, sizeof(int)));
  516. write(s, boost::asio::buffer(crypto_name));
  517. int file_content_size = 0;
  518. read(s, boost::asio::buffer(&file_content_size, sizeof(int)));
  519. #ifdef DEBUG
  520. std::cout << "Optimizer: File contents size " << file_content_size << std::endl;
  521. #endif
  522. char file_content[file_content_size + 1];
  523. file_content_size = read(s, boost::asio::buffer(file_content, file_content_size));
  524. file_content[file_content_size] = '\0';
  525. makeAbsAndPrecomputeCaches(file_content);
  526. }
  527. catch(std::exception const& ex)
  528. {
  529. cout << "Optimizer: No server, using reference values for " << crypto_system->toString() << endl;
  530. abs_cache.clear();
  531. precompute_cache.clear();
  532. set<string> crypto_params_set;
  533. crypto_system->getAllCryptoParams(crypto_params_set);
  534. for (auto crypto_param : crypto_params_set)
  535. {
  536. abs_cache[crypto_param] = crypto_system->estimateAbsTime(crypto_param);
  537. precompute_cache[crypto_param] = crypto_system->estimatePrecomputeTime(crypto_param);
  538. }
  539. }
  540. }
  541. void PIROptimizer::makeAbsAndPrecomputeCaches(char* serialized_cache)
  542. {
  543. abs_cache.clear();
  544. precompute_cache.clear();
  545. std::vector<string> lines;
  546. boost::algorithm::split(lines, serialized_cache, boost::algorithm::is_any_of("\n"));
  547. for (auto line : lines)
  548. {
  549. std::cout << line << std::endl;
  550. vector<string> fields;
  551. boost::algorithm::split(fields, line, boost::algorithm::is_any_of(" "));
  552. if (fields.size() == 3)
  553. {
  554. abs_cache[fields.at(0)] = atof(fields.at(1).c_str());
  555. precompute_cache[fields.at(0)] = atof(fields.at(2).c_str());
  556. }
  557. else if (line.find_first_not_of(' ') != std::string::npos)// if it is not a blank line
  558. {
  559. std::cout << "PIROptimizer: Absorption and precompute cache corrupted" << std::endl;
  560. std::cout << "PIROptimizer: Remove files in exp/*.abs in the server before proceeding" << std::endl;
  561. exit(1);
  562. }
  563. }
  564. }
  565. /**
  566. * Compute the time for generate the complete query.
  567. * Params :
  568. * - unsigned int d : dimension ;
  569. * - crypto_ptr crypto : HomomorphicCrypto shared_ptr ;
  570. *
  571. * Return :
  572. * - double : Complete query duration.
  573. **/
  574. double PIROptimizer::getGenQueryCost(unsigned int d, unsigned int *dn)
  575. {
  576. double GenQ = 0.0;
  577. bool shortversion = true;
  578. string current_crypto_params = currentPublicParams->getSerializedParams(shortversion);
  579. for (unsigned int i = 0 ; i < d ; i++)
  580. {
  581. GenQ += dn[i] * encrypt_cache[current_crypto_params];
  582. }
  583. return GenQ;
  584. }
  585. /**
  586. * Get time to encrypt a query for a given dimension.
  587. * Params :
  588. * - unsigned int d : dimension ;
  589. * - crypto_ptr crypto : HomomorphicCrypto shared_ptr ;
  590. *
  591. * Return :
  592. * - double : Duration in seconds.
  593. **/
  594. double PIROptimizer::getQueryElemGenCost(unsigned int d, crypto_ptr crypto)
  595. {
  596. double start, stop;
  597. double elapsed_time = 0;
  598. unsigned int query_elts_nbr = 0;
  599. PIRParameters pirParams;
  600. pirParams.d = d;
  601. pirParams.alpha = 1;
  602. for (unsigned int i = 0 ; i < d; i++)
  603. pirParams.n[i] = 1;
  604. // Needed for calls to getAbsorptionBitsize
  605. crypto->setandgetAbsBitPerCiphertext(1); // Set internally best absorption possible
  606. PIRQueryGenerator_internal queryGenerator(pirParams, *crypto);
  607. queryGenerator.setChosenElement(1);
  608. do{
  609. for (unsigned int i = 0 ; i < d; i++) pirParams.n[i] *= 2;
  610. start = omp_get_wtime();
  611. queryGenerator.generateQuery();
  612. stop = omp_get_wtime();
  613. queryGenerator.cleanQueryBuffer();
  614. }while((elapsed_time = (stop - start)) <= 0.5);
  615. for(unsigned int i = 0 ; i < d ; i++) query_elts_nbr += pirParams.n[i];
  616. double result = elapsed_time / query_elts_nbr;
  617. return result;
  618. }
  619. const PIRParameters& PIROptimizer::getParameters()
  620. {
  621. return pirParameters;
  622. }
  623. double PIROptimizer::getAbs1PlaintextTime(HomomorphicCrypto* crypto_system)
  624. {
  625. bool shortversion = true;
  626. return abs_cache[currentPublicParams->getSerializedParams(shortversion)];
  627. }
  628. double PIROptimizer::getPrecompute1PlaintextTime(HomomorphicCrypto* crypto_system)
  629. {
  630. bool shortversion = true;
  631. return precompute_cache[currentPublicParams->getSerializedParams(shortversion)];
  632. }
  633. /**
  634. * Return natural aggregation value.
  635. **/
  636. unsigned int PIROptimizer::getMinAlpha()
  637. {
  638. double r = ceil(Tp(0) / static_cast<double>(fixedVars.l));
  639. r = (r > fixedVars.n) ? fixedVars.n : r;
  640. unsigned ret = (r < 1.0) ? 1 : unsigned(r);
  641. /*No limit for agregation*/
  642. if (fixedVars.alphaMax == 0)
  643. return ret;
  644. else
  645. return min(ret, fixedVars.alphaMax);
  646. }
  647. unsigned int PIROptimizer::getMaxAlpha()
  648. {
  649. return ((fixedVars.alphaMax == 0) || (fixedVars.n < fixedVars.alphaMax)) ? fixedVars.n : fixedVars.alphaMax;
  650. }
  651. /**
  652. * Given the fixed vars and the choices done in the optimize loop, estimate costs.
  653. * If total cost is better than the previus optimum, replace it.
  654. * Return total cost of the PIR retrieval (given the fitness method in vars).
  655. **/
  656. double PIROptimizer::computeOptimVars(unsigned int alpha, unsigned int d, OptimVars& vars)
  657. {
  658. if (alpha == 0) alpha = 1;
  659. unsigned int dn[fixedVars.dMax];
  660. // Compute dimension sizes given the number of files, aggregation, and recursion levels
  661. getDimSize(fixedVars.n, alpha, d, dn);
  662. //Save alpha and d values.
  663. vars.setAlpha(alpha);
  664. vars.setDim(d);
  665. crypto->setandgetAbsBitPerCiphertext(dn[0]);
  666. // Get costs for query generation, emission, reply generation, reception, and decryption
  667. vars.setGenQ(getGenQueryCost(d, dn));
  668. vars.setSendQ(getSendCost(fixedVars.Tupc, fixedVars.Tdos, nbc, d, dn));
  669. vars.setGenR(getReplyGenCost(alpha, d, dn));
  670. vars.setSendR(getReceiveCost(alpha, fixedVars.Tups, fixedVars.Tdoc, nbc, d));
  671. vars.setDecR(getDecryptCost(alpha, d));
  672. // Decide whether this test is better than the optimum. If so replace it.
  673. analyseResult(alpha, d, vars);
  674. // Return total cost of the PIR retrieval (given the fitness method in vars)
  675. return vars.getValue();
  676. }
  677. void PIROptimizer::processResults(unsigned int i)
  678. {
  679. if (silent == false) showBestResults(i);
  680. writeTestBestResult(i);
  681. }
  682. /**
  683. * Test current result to get the optimum.
  684. **/
  685. void PIROptimizer::analyseResult(unsigned int alpha, unsigned int d, OptimVars& vars)
  686. {
  687. bool shortversion = true;
  688. // < operator is defined in OptimVars object, compares total cost value
  689. if(vars < optimum)
  690. {
  691. // If vars is better than the current optimum redefine it
  692. optimum = vars;
  693. // Get a final version of the parameters with the absorption size
  694. optimum.crypto_params = crypto->getSerializedCryptoParams(!shortversion);
  695. #ifdef DEBUG
  696. cout << "PIROptimizer: New optimum cryptoparams " << optimum.crypto_params << endl;
  697. #endif
  698. }
  699. }
  700. void PIROptimizer::getFixedVarsFromServer(FixedVars& fixed_vars, boost::asio::ip::tcp::socket& s)
  701. {
  702. try{
  703. /*Send command*/
  704. size_t cmd = DATA;
  705. write(s, boost::asio::buffer(&cmd, sizeof(cmd)));
  706. /*Get number of files*/
  707. read(s, boost::asio::buffer(&fixed_vars.n, sizeof(fixed_vars.n)));
  708. cout << "Optimizer: Number of files is " << fixed_vars.n << endl;
  709. /*Get max file size*/
  710. read(s, boost::asio::buffer(&fixed_vars.l, sizeof(fixed_vars.l)));
  711. cout << "Optimizer: File bytesize is " << fixed_vars.l << endl;
  712. // fixed_vars.l should be in bits
  713. fixed_vars.l*=8;
  714. }catch(const std::exception& ex)
  715. {
  716. std::cerr << "Catched exception in " << __FUNCTION__ << ": " << ex.what() << std::endl;
  717. }
  718. }
  719. /**
  720. * Print the best result.
  721. **/
  722. void PIROptimizer::showBestResults(unsigned int i)
  723. {
  724. writeBigMessage(" RESULTS exp " + to_string(i+1) + " ");
  725. cout << "\tparams : " << optimum.crypto_params << endl;
  726. cout << "\td : " << optimum.getDim() << endl;
  727. cout << "\talpha : " << optimum.getAlpha() << endl;
  728. cout << "\tGenQ : " << optimum.getGenQ() << " sec" << endl;
  729. cout << "\tSenQ : " << optimum.getSendQ() << " sec" << endl;
  730. cout << "\tGenR : " << optimum.getGenR() << " sec" << endl;
  731. cout << "\tSendR : " << optimum.getSendR() << " sec" << endl;
  732. cout << "\tDecR : " << optimum.getDecR() << " sec" << endl;
  733. cout << "\tTotal Time : "<< optimum.getValue() << " sec" << endl;
  734. cout << "\t--Fixed Vars-- " << endl;
  735. cout << "\tl : " << fixedVars.l << " bits"<< endl;
  736. cout << "\tn : " << fixedVars.n << endl;
  737. cout << "\tTupc/dos : "<< fixedVars.Tupc << " b/s" << endl;
  738. cout << "\tTdoc/ups : "<< fixedVars.Tdoc << " b/s" << endl;
  739. writeBigMessage("##############");
  740. }
  741. void PIROptimizer::getPreComputedData(HomomorphicCrypto* crypto)
  742. {
  743. std::string fdec_path(OptimService::folderName + OptimService::fileName + crypto->toString()
  744. + OptimService::decFileExtension);
  745. std::string fenc_path(OptimService::folderName + OptimService::fileName + crypto->toString()
  746. + OptimService::encFileExtension);
  747. decrypt_cache.clear();
  748. encrypt_cache.clear();
  749. set<string> crypto_params_set;
  750. crypto->getAllCryptoParams(crypto_params_set);
  751. //if (OptimService::verifyOptimData(crypto_params_set, fdec_path, fenc_path))
  752. if (OptimService::fileOutdated(crypto->toString(), OptimService::encFileExtension) ||
  753. OptimService::fileOutdated(crypto->toString(), OptimService::decFileExtension) )
  754. {
  755. cout << "PIROptimizer:: Computing cache data for " << crypto->toString() << ", this can take a while..." << endl;
  756. computeOptimData(crypto_params_set);
  757. cout << "PIROptimizer: Finished computing cache data for "<< crypto->toString() << endl;
  758. }
  759. else
  760. {
  761. OptimService::readOptimData(decrypt_cache, fdec_path);
  762. OptimService::readOptimData(encrypt_cache, fenc_path);
  763. }
  764. }
  765. void PIROptimizer::computeOptimData(set<string> &crypto_params_set )
  766. {
  767. unsigned int i = 1;
  768. unsigned int crypto_params_nbr = crypto_params_set.size();
  769. string optim_data2write;
  770. // Generate the encryption performance cache
  771. std::string file_path(OptimService::folderName + OptimService::fileName + crypto->toString());
  772. for (auto crypto_param : crypto_params_set)
  773. {
  774. cout << "PIROptimizer: Encrypt cache generation for " << crypto_param << " " << i++
  775. << "/" << crypto_params_nbr << "." << flush;
  776. crypto->setNewParameters(crypto_param);
  777. cout << "." << endl;
  778. encrypt_cache[crypto_param] = getQueryElemGenCost(1, crypto);
  779. std::ostringstream out;
  780. out << std::setprecision(kPrecision) << encrypt_cache[crypto_param];
  781. optim_data2write += crypto_param + " " + out.str() + "\n";
  782. }
  783. if(OptimService::writeOptimDataBuffer(optim_data2write, file_path+OptimService::encFileExtension))
  784. {
  785. std::cout << "PIROptimizer: Error when writing optimization data, aborting." << std::endl;
  786. exit(1);
  787. }
  788. optim_data2write = "";
  789. i=1;
  790. // Generate the decryption performance cache
  791. for (auto crypto_param : crypto_params_set)
  792. {
  793. cout << "PIROptimizer: Decrypt cache generation for " << crypto_param << " " << i++
  794. << "/" << crypto_params_nbr << "." << flush;
  795. crypto->setNewParameters(crypto_param);
  796. cout << "." << endl;
  797. decrypt_cache[crypto_param] = getDecCost(1, crypto);
  798. std::ostringstream out;
  799. out << std::setprecision(kPrecision) << decrypt_cache[crypto_param];
  800. optim_data2write += crypto_param + " " + out.str() + "\n";
  801. }
  802. if(OptimService::writeOptimDataBuffer(optim_data2write, file_path+OptimService::decFileExtension))
  803. {
  804. std::cout << "PIROptimizer: Error when writing optimization data, aborting." << std::endl;
  805. exit(1);
  806. }
  807. }
  808. /**
  809. * Writes values of the best result.
  810. **/
  811. void PIROptimizer::writeTestBestResult(unsigned int i)
  812. {
  813. OptimService::writeMessage(i, "### BEST RESULT ###");
  814. OptimService::writeFootFile(i);
  815. OptimService::writeConfigFile(getMinAlpha(), optimum.getAlpha(), optimum.getDim(), i);
  816. }
  817. /**
  818. * Disconect to the sever's optimizer.
  819. **/
  820. void PIROptimizer::disconnect()
  821. {
  822. try
  823. {
  824. size_t cmd = EXIT;
  825. s.send(boost::asio::buffer(&cmd, sizeof(size_t)));// exit function
  826. s.close();
  827. }catch (const std::exception& ex)
  828. {}
  829. }
  830. void PIROptimizer::connect()
  831. {
  832. try
  833. {
  834. tcp::endpoint endpoint(boost::asio::ip::address::from_string(serv_ip), server_optimport);
  835. s.connect(endpoint);
  836. }catch(const std::exception& ex )
  837. {
  838. }
  839. }
  840. /**
  841. * Print "big" message on the terminal.
  842. **/
  843. void PIROptimizer::writeBigMessage(string msg)
  844. {
  845. std::transform(msg.begin(), msg.end(), msg.begin(), ::toupper);
  846. cout << "########" << msg << "########" << endl;
  847. }
  848. PIROptimizer::~PIROptimizer()
  849. {
  850. }
  851. /**
  852. * Do a network speed test with the server.
  853. * Params :
  854. * - int port : server port
  855. **/
  856. void PIROptimizer::getNetworkInfos(FixedVars& fixedVars, boost::asio::ip::tcp::socket& s)
  857. {
  858. double start = 0, end=0, loop = 1;
  859. char *msg = (char *) malloc(MEGA_BYTE+4);
  860. memset(msg, 1, MEGA_BYTE);
  861. size_t cmd = SPEED;
  862. /*Send command*/
  863. write(s, boost::asio::buffer(&cmd, sizeof(size_t)));
  864. // Do upload test
  865. start = omp_get_wtime();
  866. for (int i = 0 ; i < loop ; i++) write(s, boost::asio::buffer(msg, MEGA_BYTE));
  867. end = omp_get_wtime();
  868. // Ignore it if value was forced
  869. if (fixedVars.Tupc != 0)
  870. {
  871. cout << "Optimizer: Upload speed value forced, dropping network test result" << std::endl;
  872. }
  873. else
  874. {
  875. fixedVars.Tupc = (loop /(end - start)) * 8 * MEGA_BYTE;
  876. fixedVars.Tdos = fixedVars.Tupc;
  877. cout << "Optimizer: Upload speed test gives " << fixedVars.Tupc << " bits/s" << endl;
  878. }
  879. // Do download tests
  880. start = omp_get_wtime();
  881. for (int i = 0 ; i < loop ; i++) read(s, boost::asio::buffer(msg, MEGA_BYTE));
  882. end = omp_get_wtime();
  883. if (fixedVars.Tdoc != 0)
  884. {
  885. cout << "Optimizer: Download speed value forced, dropping network test result" << endl;
  886. }
  887. else
  888. {
  889. fixedVars.Tdoc = loop / (end - start) * 8 * MEGA_BYTE;
  890. fixedVars.Tups = fixedVars.Tdoc;
  891. cout << "Optimizer: Download speed test gives " << fixedVars.Tdoc << " bits/s" << endl;
  892. }
  893. free(msg);
  894. }
  895. void PIROptimizer::sendExitCommand(boost::asio::ip::tcp::socket& s)
  896. {
  897. try{
  898. size_t cmd = EXIT;
  899. write(s, boost::asio::buffer(&cmd, sizeof(cmd)));
  900. }catch(std::exception const& ex)
  901. {
  902. cerr << "Error when sending EXIT command to the server: " << ex.what() << endl;
  903. }
  904. }