/* Copyright (C) 2014 Carlos Aguilar Melchor, Joris Barrier, Marc-Olivier Killijian * This file is part of XPIR. * * XPIR is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * XPIR is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XPIR. If not, see . */ #include "PIROptimizer.hpp" static const unsigned int kPrecision = 5; using boost::asio::ip::tcp; PIROptimizer::PIROptimizer(DBHandler *db) : pir("lipmaa"), filePath("optim/preCompute.abs") { fileCount = db->getNbStream(); maxFileBytesize = db->getmaxFileBytesize(); } PIROptimizer::~PIROptimizer() {} void PIROptimizer::speedTest(boost::asio::ip::tcp::socket& s) { try { unsigned int loop = 1; char* msg = (char *) malloc(MEGA_BYTE); boost::system::error_code error; double start = omp_get_wtime(); for (unsigned int i = 0 ; i < loop ; i++) boost::asio::read(s, boost::asio::buffer(msg, MEGA_BYTE)); cout << "PIROptimizer: Speed test gives upload=" << double(loop / (omp_get_wtime() - start) ) << " MB/s"; start = omp_get_wtime(); for (unsigned int i = 0 ; i < loop ; i++) write(s, boost::asio::buffer(msg, MEGA_BYTE)); cout << ", download=" << double(loop / (omp_get_wtime() - start) ) << " MB/s" << endl; free(msg); } catch(std::exception const& ex) { cout << "Error : " << ex.what() << endl; } } unsigned int PIROptimizer::sendAbsAndPrecomputeCaches(boost::asio::ip::tcp::socket& s) { try { unsigned int crypto_name_size = 0; read(s, boost::asio::buffer(&crypto_name_size, sizeof(int))); char crypto_name[crypto_name_size + 1]; crypto_name_size = read(s, boost::asio::buffer(crypto_name, crypto_name_size)); crypto_name[crypto_name_size] = '\0'; cout << "PIROptimizer: Sending absorption and precompute costs for " << crypto_name << endl; std::string file_path(OptimService::folderName + OptimService::fileName + crypto_name + OptimService::absFileExtension); std::string file_content; if (OptimService::readEntireFile(file_content, file_path) < 0) { cout << "PIROptimizer: Error when reading file : " << file_path << ", abort." << endl; return 0;} int file_content_size = file_content.size(); #ifdef DEBUG std::cout << "PIROptimizer: Absorption and precompute costs file size " << file_content_size << std::endl; #endif write(s, boost::asio::buffer(&file_content_size, sizeof(file_content_size))); write(s, boost::asio::buffer(file_content)); } catch(std::exception const& ex) { cout << "Error : " << ex.what() << endl; } return 0; } /** * Build cache if necessary. **/ void PIROptimizer::prepareOptimData() { for (auto crypto_name : HomomorphicCryptoFactory_internal::crypto_method_name_vec) { std::string file_path(OptimService::folderName + OptimService::fileName + crypto_name + OptimService::absFileExtension); if (OptimService::fileOutdated(crypto_name, OptimService::absFileExtension)) { std::cout << "PIROptimizer: Absorption and precompute performance cache is outdated, regenerating it" << std::endl; std::string optim_data2write = computeOptimData(crypto_name); if(OptimService::writeOptimDataBuffer(optim_data2write, file_path)) {std::cout << "PIROptimizer: Error when writing optimization data, aborting." << std::endl; exit(1);} std::cout << "PIROptimizer: Finished generating the absorption and precompute performance cache" << std::endl; } } } std::string PIROptimizer::computeOptimData(const std::string& crypto_name) { double abs1plaintext_time, precompute1plaintext_time; GenericPIRReplyGenerator* generator_ptr = PIRReplyGeneratorFactory::getPIRReplyGenerator(crypto_name); HomomorphicCrypto* crypto_ptr = HomomorphicCryptoFactory_internal::getCrypto(crypto_name); std::set crypto_params_set; string optim_data2write; generator_ptr->setCryptoMethod(crypto_ptr); crypto_ptr->getAllCryptoParams(crypto_params_set); for (auto crypto_param : crypto_params_set) { std::cout << "PIROptimizer: Generating cache for " << crypto_param << std::endl; crypto_ptr->getPublicParameters().computeNewParameters(crypto_param); crypto_ptr->getPublicParameters().setMockedPubKey(); abs1plaintext_time = getAbs1PlaintextTime(crypto_ptr, generator_ptr); precompute1plaintext_time = getPrecompute1PlaintextTime(crypto_ptr, generator_ptr); std::ostringstream out; out << std::setprecision(kPrecision) << abs1plaintext_time << " " << precompute1plaintext_time; optim_data2write += crypto_param + " " + out.str() + "\n"; } delete generator_ptr; delete crypto_ptr; return optim_data2write; } double PIROptimizer::getAbs1PlaintextTime(HomomorphicCrypto* crypto_ptr, GenericPIRReplyGenerator* generator) { double result; uint64_t plaintext_nbr; PIRParameters pir_params; pir_params.d = 1; pir_params.alpha = 1; pir_params.n[0] = 4; crypto_ptr->setandgetAbsBitPerCiphertext(pir_params.n[0]); // Set best absorption possible plaintext_nbr = 4; do { generator->mutex.try_lock(); generator->mutex.unlock(); result = generator->generateReplySimulation(pir_params, plaintext_nbr); plaintext_nbr *= 2; } while(result < 0.5 && plaintext_nbr*pir_params.n[0]*crypto_ptr->getPublicParameters().getCiphertextBitsize() < (1UL<<30)); plaintext_nbr /= 2; double plaintexts_in_database = (double)pir_params.n[0] * plaintext_nbr; return result / plaintexts_in_database; } double PIROptimizer::getPrecompute1PlaintextTime(HomomorphicCrypto* crypto_ptr, GenericPIRReplyGenerator* generator) { double result; uint64_t plaintext_nbr; PIRParameters pir_params; pir_params.d = 1; pir_params.alpha = 1; pir_params.n[0] = 4; crypto_ptr->setandgetAbsBitPerCiphertext(pir_params.n[0]); // Set best absorption possible plaintext_nbr = 128; do { generator->mutex.try_lock(); generator->mutex.unlock(); result = generator->precomputationSimulation(pir_params, plaintext_nbr); plaintext_nbr *= 2; } while(result < 0.5 && result != 0.0 && plaintext_nbr*pir_params.n[0]*crypto_ptr->getPublicParameters().getCiphertextBitsize() < (1UL<<30)); plaintext_nbr /= 2; double plaintexts_in_database = (double)pir_params.n[0] * plaintext_nbr; return result / plaintexts_in_database; } void PIROptimizer::serve() { tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), COMMAND_AND_CONTROL_PORT)); acceptor.listen(5); cout << "Waiting client..." << endl; while(true) { boost::asio::ip::tcp::socket socket(acceptor.get_io_service()); acceptor.accept(socket); // boost::thread t(boost::bind(&PIROptimizer::controlAndCommand, this, socket)); controlAndCommand(socket); } } void PIROptimizer::sendDatabaseInfos(boost::asio::ip::tcp::socket&s) { cout << "PIROptimizer: Sending database infos file_count=" << fileCount << ", max_bytesize=" << maxFileBytesize << endl; try{ /*Send number of files*/ boost::asio::write(s, boost::asio::buffer(&fileCount, sizeof(fileCount))); /*Send max file size*/ boost::asio::write(s, boost::asio::buffer(&maxFileBytesize, sizeof(maxFileBytesize))); }catch(std::exception const& ex) { cerr << "Error when sending database informations : " << ex.what() << std::endl; } } void PIROptimizer::controlAndCommand(boost::asio::ip::tcp::socket& s) { size_t cmd = NOP; try{ while(cmd != EXIT) { boost::asio::read(s, boost::asio::buffer(&cmd, sizeof(size_t))); switch (cmd) { case ABS : { sendAbsAndPrecomputeCaches(s); break; } case SPEED : { speedTest(s); break; } case EXIT : { cout << "PIROptimizer: EXIT command recieved" << endl; break; } case DATA : { sendDatabaseInfos(s); break; } default : { cout << "PIROptimizer: Looping 30s as requested by client ..." << endl; sleep(30); break; } } } }catch(std::exception const& ex) { cerr << "Client quit without send EXIT code: " << ex.what() << endl; } } void PIROptimizer::optimize() { serve(); }