simplePIR.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #include "libpir.hpp"
  2. #include "apps/server/DBDirectoryProcessor.hpp"
  3. bool run(DBHandler *db, uint64_t chosen_element, PIRParameters params){
  4. /******************************************************************************
  5. * PIR and Crypto Setup (must be done by both the client and the server)
  6. * In a real application the client and server must agree on the parameters
  7. * For example the client chooses and sends them to the server (or inversely)
  8. ******************************************************************************/
  9. HomomorphicCrypto *crypto = HomomorphicCryptoFactory::getCryptoMethod(params.crypto_params);
  10. // Absorption capacity of an LWE encryption scheme depends on the number of sums that are going
  11. // to be done in the PIR protocol, it must therefore be initialized
  12. // Warning here we suppose the biggest dimension is in d[0]
  13. // otherwise absorbtion needs to be computed accordingly
  14. crypto->setandgetAbsBitPerCiphertext(params.n[0]);
  15. /******************************************************************************
  16. * Query generation phase (client-side)
  17. ******************************************************************************/
  18. // Create the query generator object
  19. PIRQueryGenerator q_generator(params,*crypto);
  20. std::cout << "SimplePIR: Generating query ..." << std::endl;
  21. // Generate a query to get the FOURTH element in the database (indexes begin at 0)
  22. // Warning : if we had set params.alpha=2 elements would be aggregated 2 by 2 and
  23. // generatequery would only accept as input 0 (the two first elements) or 1 (the other two)
  24. q_generator.generateQuery(chosen_element);
  25. std::cout << "SimplePIR: Query generated" << std::endl;
  26. /******************************************************************************
  27. * Reply generation phase (server-side)
  28. ******************************************************************************/
  29. // Create the reply generator object
  30. // We could have also defined PIRReplyGenerator *r_generator(params,*crypto,db);
  31. // But we prefer a pointer to show (below) how to use multiple generators for a given db
  32. PIRReplyGenerator *r_generator = new PIRReplyGenerator(params,*crypto,db);
  33. r_generator->setPirParams(params);
  34. // In a real application the client would pop the queries from q with popQuery and
  35. // send them through the network and the server would receive and push them into s
  36. // using pushQuery
  37. char* query_element;
  38. while (q_generator.popQuery(&query_element))
  39. {
  40. r_generator->pushQuery(query_element);
  41. }
  42. // Import database
  43. // This could have been done on the "Database setup" phase if:
  44. // - the contents are static
  45. // - AND the imported database fits in RAM
  46. // - AND the server knows in advance the PIR and crypto parameters (e.g. chosen by him)
  47. std::cout << "SimplePIR: Importing database ..." << std::endl;
  48. // Warning aggregation is dealt with internally the bytes_per_db_element parameter here
  49. // is to be given WITHOUT multiplying it by params.alpha
  50. imported_database* imported_db = r_generator->importData(/* uint64_t offset*/ 0, /*uint64_t
  51. bytes_per_db_element */ db->getmaxFileBytesize());
  52. std::cout << "SimplePIR: Database imported" << std::endl;
  53. // Once the query is known and the database imported launch the reply generation
  54. std::cout << "SimplePIR: Generating reply ..." << std::endl;
  55. double start = omp_get_wtime();
  56. r_generator->generateReply(imported_db);
  57. double end = omp_get_wtime();
  58. std::cout << "SimplePIR: Reply generated in " << end-start << " seconds" << std::endl;
  59. /********************************************************************************
  60. * Advanced example: uncomment it to test
  61. * The object imported_db is separated from r_generator in purpose
  62. * Here is an example on how to use the same imported_db for multiple queries
  63. * DO NOT try to use the same reply generator more than once, this causes issues
  64. * ******************************************************************************/
  65. /*
  66. // Generate 3 replies from 3 queries
  67. for (int i = 0 ; i < 3 ; i++){
  68. // Pop (and drop for this simple example) the generated reply
  69. char* reply_element_tmp;
  70. while (r_generator->popReply(&reply_element_tmp)){
  71. free(reply_element_tmp);
  72. }
  73. // Free memory and the r_generator object
  74. r_generator->freeQueries();
  75. delete r_generator;
  76. // Create and initialize a new generator object
  77. r_generator = new PIRReplyGenerator(params,*crypto,db);
  78. r_generator->setPirParams(params);
  79. // Generate a new query
  80. q_generator.generateQuery(chosen_element);
  81. // Push it to the reply generator
  82. while (q_generator.popQuery(&query_element))
  83. {
  84. r_generator->pushQuery(query_element);
  85. }
  86. // Generate again the reply
  87. r_generator->generateReply(imported_db);
  88. }
  89. */
  90. /******************************************************************************
  91. * Reply extraction phase (client-side)
  92. ******************************************************************************/
  93. PIRReplyExtraction *r_extractor=new PIRReplyExtraction(params,*crypto);
  94. // In a real application the server would pop the replies from s with popReply and
  95. // send them through the network together with nbRepliesGenerated and aggregated_maxFileSize
  96. // and the client would receive the replies and push them into r using pushEncryptedReply
  97. std::cout << "SimplePIR: "<< r_generator->getnbRepliesGenerated()<< " Replies generated " << std::endl;
  98. uint64_t clientside_maxFileBytesize = db->getmaxFileBytesize();
  99. char* reply_element;
  100. while (r_generator->popReply(&reply_element))
  101. {
  102. r_extractor->pushEncryptedReply(reply_element);
  103. }
  104. std::cout << "SimplePIR: Extracting reply ..." << std::endl;
  105. r_extractor->extractReply(clientside_maxFileBytesize);
  106. std::cout << "SimplePIR: Reply extracted" << std::endl;
  107. // In a real application instead of writing to a buffer we could write to an output file
  108. char *outptr, *result, *tmp;
  109. outptr = result = (char*)calloc(r_extractor->getnbPlaintextReplies(clientside_maxFileBytesize)*r_extractor->getPlaintextReplyBytesize(), sizeof(char));
  110. while (r_extractor->popPlaintextResult(&tmp))
  111. {
  112. memcpy(outptr, tmp, r_extractor->getPlaintextReplyBytesize());
  113. outptr+=r_extractor->getPlaintextReplyBytesize();
  114. free(tmp);
  115. }
  116. // Result is in ... result
  117. /******************************************************************************
  118. * Test correctness
  119. ******************************************************************************/
  120. char *db_element = (char*)calloc(clientside_maxFileBytesize*params.alpha, sizeof(char));
  121. bool fail = false;
  122. db->readAggregatedStream(chosen_element, params.alpha, 0, clientside_maxFileBytesize, db_element);
  123. if (memcmp(result, db_element, clientside_maxFileBytesize*params.alpha))
  124. {
  125. std::cout << "SimplePIR: Test failed, the retrieved element is not correct" << std::endl;
  126. fail = true;
  127. }
  128. else
  129. {
  130. std::cout << "SimplePIR: Test succeeded !!!!!!!!!!!!!!!!!!!!!!!!" << std::endl<< std::endl;
  131. fail = false;
  132. }
  133. /******************************************************************************
  134. * Cleanup
  135. ******************************************************************************/
  136. delete imported_db;
  137. r_generator->freeQueries();
  138. delete r_generator;
  139. free(result);
  140. free(db_element);
  141. return fail;
  142. }
  143. int main(int argc, char * argv[]) {
  144. uint64_t database_size, nb_files, chosen_element, maxFileBytesize;
  145. PIRParameters params;
  146. bool tests_failed = false;
  147. /******************************************************************************
  148. * Database setup (server-side)
  149. ******************************************************************************/
  150. // To Create the database generator object
  151. // it can be a DBGenerator that simulate nb_files files of size streamBytesize
  152. // database_size = 1ULL<<25; nb_files = 4; maxFileBytesize = database_size/nb_files;
  153. // DBGenerator db(nb_files, maxFileBytesize, /*bool silent*/ false);
  154. //
  155. // OR it can be a DBDirectoryProcessor that reads a real file in the ./db directory
  156. // and splits it into nb_files virtual files
  157. // nb_files = 4;
  158. // DBDirectoryProcessor db(nb_files);
  159. // database_size = db.getDBSizeinbits();maxFileBytesize = database_size/nb_files;
  160. //
  161. // OR it can be a DBDirectoryProcessor that reads the real files in the ./db directory
  162. // DBDirectoryProcessor db;
  163. // nb_files=db.getNbStream();database_size = db.getDBSizeinbits();
  164. // maxFileBytesize = database_size/nb_files;
  165. // Simple test
  166. std::cout << "======================================================================" << std::endl;
  167. std::cout << "Test 1/7: database_size = 1ULL<<30; nb_files = 20;" << std::endl;
  168. std::cout << "params.alpha = 1; params.d = 1; crypto_params = LWE:80:2048:120;" << std::endl;
  169. std::cout << "======================================================================" << std::endl;
  170. database_size = 1ULL<<30; nb_files = 20; maxFileBytesize = database_size/nb_files;
  171. DBGenerator db(nb_files, maxFileBytesize, /*bool silent*/ false);
  172. chosen_element = 3;
  173. params.alpha = 1; params.d = 1; params.n[0] = nb_files;
  174. // The crypto parameters can be set to other values
  175. // You can get a list of all available cryptographic parameters with this function call
  176. // HomomorphicCryptoFactory::printAllCryptoParams();
  177. params.crypto_params = "LWE:80:2048:120";
  178. tests_failed |= run(&db, chosen_element, params);
  179. // Test with aggregation
  180. // WARNING we must provide the representation of the database GIVEN recursion and aggregation
  181. // as here we have 100 elements and aggregate them in a unique group we have params.n[0]=1
  182. std::cout << "======================================================================" << std::endl;
  183. std::cout << "Test 2/7: database_size = 1ULL<<25; nb_files = 100;" << std::endl;
  184. std::cout << "params.alpha = 100; params.d = 1; crypto_params = LWE:80:2048:120;" << std::endl;
  185. std::cout << "======================================================================" << std::endl;
  186. database_size = 1ULL<<25; nb_files = 100; maxFileBytesize = database_size/nb_files;
  187. DBGenerator db2(nb_files, maxFileBytesize, /*bool silent*/ false);
  188. chosen_element = 0;
  189. params.alpha = 100; params.d = 1; params.n[0] = 1;
  190. params.crypto_params = "LWE:80:2048:120";
  191. tests_failed |= run(&db2, chosen_element, params);
  192. // Test with recursion 2
  193. std::cout << "======================================================================" << std::endl;
  194. std::cout << "Test 3/7: database_size = 1ULL<<25; nb_files = 100;" << std::endl;
  195. std::cout << "params.alpha = 1; params.d = 2; crypto_params = LWE:80:2048:120;" << std::endl;
  196. std::cout << "======================================================================" << std::endl;
  197. database_size = 1ULL<<25; nb_files = 100; maxFileBytesize = database_size/nb_files;
  198. DBGenerator db3(nb_files, maxFileBytesize, /*bool silent*/ false);
  199. chosen_element = 3;
  200. params.alpha = 1; params.d = 2; params.n[0] = 50; params.n[1] = 2;
  201. params.crypto_params = "LWE:80:2048:120";
  202. tests_failed |= run(&db3, chosen_element, params);
  203. // Test with recursion 2 and aggregation
  204. std::cout << "======================================================================" << std::endl;
  205. std::cout << "Test 4/7: database_size = 1ULL<<25; nb_files = 100;" << std::endl;
  206. std::cout << "params.alpha = 2; params.d = 2; crypto_params = LWE:80:2048:120;" << std::endl;
  207. std::cout << "======================================================================" << std::endl;
  208. database_size = 1ULL<<25; nb_files = 100; maxFileBytesize = database_size/nb_files;
  209. DBGenerator db4(nb_files, maxFileBytesize, /*bool silent*/ false);
  210. chosen_element = 3;
  211. params.alpha = 2; params.d = 2; params.n[0] = 25; params.n[1] = 2;
  212. params.crypto_params = "LWE:80:2048:120";
  213. tests_failed |= run(&db4, chosen_element, params);
  214. // Test with recursion 3
  215. std::cout << "======================================================================" << std::endl;
  216. std::cout << "Test 5/7: database_size = 1ULL<<25; nb_files = 100;" << std::endl;
  217. std::cout << "params.alpha = 1; params.d = 3; crypto_params = LWE:80:2048:120;" << std::endl;
  218. std::cout << "======================================================================" << std::endl;
  219. database_size = 1ULL<<25; nb_files = 100; maxFileBytesize = database_size/nb_files;
  220. DBGenerator db5(nb_files, maxFileBytesize, /*bool silent*/ false);
  221. chosen_element = 3;
  222. params.alpha = 1; params.d = 3; params.n[0] = 5; params.n[1] = 5; params.n[2] = 4;
  223. params.crypto_params = "LWE:80:2048:120";
  224. tests_failed |= run(&db5, chosen_element, params);
  225. // Test with a DBDirectoryProcessor splitting a big real file
  226. std::cout << "======================================================================" << std::endl;
  227. std::cout << "Test 6/7: DBDirectoryProcessor with split; database_size = 1ULL<<25; nb_files = 4;" << std::endl;
  228. std::cout << "params.alpha = 1; params.d = 1; crypto_params = LWE:80:2048:120;" << std::endl;
  229. std::cout << "======================================================================" << std::endl;
  230. database_size = 1ULL<<25; nb_files = 4; maxFileBytesize = database_size/nb_files;
  231. DBDirectoryProcessor db6(/*split the first file in*/ nb_files /*files*/);
  232. if (db6.getErrorStatus()==true){
  233. std::cout << "SimplePIR : Error with db directory skipping test ..." << std::endl << std::endl;
  234. } else {
  235. chosen_element = 3;
  236. params.alpha = 1; params.d = 1; params.n[0] = nb_files;
  237. params.crypto_params = "LWE:80:2048:120";
  238. tests_failed |= run(&db6, chosen_element, params);
  239. }
  240. // Test with a DBDirectoryProcessor reading real files
  241. std::cout << "======================================================================" << std::endl;
  242. std::cout << "Test 7/7: DBDirectoryProcessor without split;" << std::endl;
  243. std::cout << "params.alpha = 1; params.d = 1; crypto_params = LWE:80:2048:120;" << std::endl;
  244. std::cout << "======================================================================" << std::endl;
  245. DBDirectoryProcessor db7;
  246. if (db6.getErrorStatus()==true){
  247. std::cout << "SimplePIR : Error with db directory skipping test ..." << std::endl << std::endl;
  248. } else {
  249. database_size = db7.getDBSizeBits()/8; nb_files = db7.getNbStream();
  250. maxFileBytesize = database_size/nb_files;
  251. chosen_element = 0;
  252. params.alpha = 1; params.d = 1; params.n[0] = nb_files;
  253. params.crypto_params = "LWE:80:2048:120";
  254. tests_failed |= run(&db7, chosen_element, params);
  255. }
  256. if (tests_failed)
  257. {
  258. std::cout << "WARNING : at least one tests failed" << std::endl;
  259. return 1;
  260. }
  261. else
  262. {
  263. std::cout << "All tests succeeded" << std::endl;
  264. return 0;
  265. }
  266. }