PIROptimizer.cpp 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  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. unsigned int read_size = 0;
  861. memset(msg, 1, MEGA_BYTE);
  862. size_t cmd = SPEED;
  863. /*Send command*/
  864. write(s, boost::asio::buffer(&cmd, sizeof(size_t)));
  865. // Do upload test
  866. start = omp_get_wtime();
  867. for (int i = 0 ; i < loop ; i++) write(s, boost::asio::buffer(msg, MEGA_BYTE));
  868. end = omp_get_wtime();
  869. // Ignore it if value was forced
  870. if (fixedVars.Tupc != 0)
  871. {
  872. cout << "Optimizer: Upload speed value forced, dropping network test result" << std::endl;
  873. }
  874. else
  875. {
  876. fixedVars.Tupc = (loop /(end - start)) * 8 * MEGA_BYTE;
  877. fixedVars.Tdos = fixedVars.Tupc;
  878. cout << "Optimizer: Upload speed test gives " << fixedVars.Tupc << " bits/s" << endl;
  879. }
  880. // Do download tests
  881. start = omp_get_wtime();
  882. for (int i = 0 ; i < loop ; i++) read(s, boost::asio::buffer(msg, MEGA_BYTE));
  883. end = omp_get_wtime();
  884. if (fixedVars.Tdoc != 0)
  885. {
  886. cout << "Optimizer: Download speed value forced, dropping network test result" << endl;
  887. }
  888. else
  889. {
  890. fixedVars.Tdoc = loop / (end - start) * 8 * MEGA_BYTE;
  891. fixedVars.Tups = fixedVars.Tdoc;
  892. cout << "Optimizer: Download speed test gives " << fixedVars.Tdoc << " bits/s" << endl;
  893. }
  894. free(msg);
  895. }
  896. void PIROptimizer::sendExitCommand(boost::asio::ip::tcp::socket& s)
  897. {
  898. try{
  899. size_t cmd = EXIT;
  900. write(s, boost::asio::buffer(&cmd, sizeof(cmd)));
  901. }catch(std::exception const& ex)
  902. {
  903. cerr << "Error when sending EXIT command to the server: " << ex.what() << endl;
  904. }
  905. }