/* 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 "NFLLWE.hpp" #include "NFLLWESecurityEstimated.hpp" #include #include #include #include #include //#define bench //#define Repetition 10000 void NFLLWE_DEBUG_MESSAGE(const char *s,poly64 p, unsigned int n){ #ifdef CRYPTO_DEBUG std::cout< fields; boost::algorithm::split(fields, crypto_param_descriptor, boost::algorithm::is_any_of(":")); setsecurityBits(atoi(fields[1].c_str())); polyDegree_ = atoi(fields[2].c_str()); aggregatedModulusBitsize_ = atoi(fields[3].c_str()); // Does the fourth parameter exist ? If so set it if (fields.size() >= 5) abspc_bitsize = atoi(fields[4].c_str()); setNewParameters(polyDegree_,aggregatedModulusBitsize_, abspc_bitsize); } // The setNewParameters method does the actual parameterization of the crypto object // it sets the alreadyInit attribute to reflects this void NFLLWE::setNewParameters(unsigned int polyDegree_, unsigned int aggregatedModulusBitsize_, int absPCBitsize_) { // Our public parameters need a pointer on us publicParams.setcrypto_container(this); // We still need to transfer this two attributes to the crypto_object // for the transition towards public parameter elimination publicParams.setAbsPCBitsize(absPCBitsize_); publicParams.setnoiseUB(5*getsecurityBits()/2); //#ifdef DEBUG // std::cout << "Security bits " << getsecurityBits()<a = (poly64) calloc(polyDegree * 2 * nbModuli, sizeof(uint64_t)); c->b = c->a + polyDegree * nbModuli; // tmpa and tmpb are used to access the nbModuli polynoms of the CRT poly64 tmpa = c->a; poly64 tmpb = c->b; poly64 tmpm = m; // b = (a*s) % f + e * A + m; // Noise creation uint64_t Berr=publicParams.getnoiseUB(); uint64_t A_bits= publicParams.getAbsorptionBitsize() / publicParams.getpolyDegree(); // We deal with the nbModuli polynoms at once because the noise is the same size for all of them nflInstance.setBoundedRandomPoly(c->b, 2*Berr-1, !uniform); NFLLWE_DEBUG_MESSAGE("Noise used: ",c->b, 4); #ifdef CRYPTO_DEBUG std::cout << "NFLLWE: Noise amplifier: " << A_bits << std::endl; #endif // Adjustments and addition to plaintext for(unsigned short currentModulus=0;currentModulusmoduli[currentModulus]) tmpb[i]-=moduli[currentModulus]; } tmpb+=polyDegree; tmpm+=polyDegree; } tmpb=c->b; NFLLWE_DEBUG_MESSAGE("Amplified noise and message: ",c->b, 4); // Noise and plaintext are the only things that are not yet in the NTT space nflInstance.nttAndPowPhi(c->b); // We still have to get a. No NTT needed because uniformly taken nflInstance.setBoundedRandomPoly(tmpa, 0, uniform); #ifdef DEBUG poly64 tmp = (poly64) calloc(polyDegree*nbModuli, sizeof(uint64_t)); #endif for(unsigned short currentModulus=0;currentModulusa, 4); NFLLWE_DEBUG_MESSAGE("Ciphertext b: ",c->b, 4); } void NFLLWE::dec(poly64 m, lwe_cipher *c) { uint64_t A_bits = publicParams.getAbsorptionBitsize() / publicParams.getpolyDegree(); const uint64_t bitmask = (1ULL<a; poly64 tmpb=c->b; poly64 tmpm=m; for(unsigned short currentModulus=0;currentModulus1) { mpz_t *tmprez=nflInstance.poly2mpz(tmpm); // If e*A+m < p/2 we mask the message bits: bitmask = (1ULL< p/2 we do a little trick to avoid signed integers and modulus reduction // e[i]= e[i] + 2**61 - p (we replace p by 2**61) and then bitmask the message. mpz_t magicConstz; mpz_init(magicConstz); mpz_ui_pow_ui(magicConstz, 2, (kModulusBitsize + 1) * nbModuli); mpz_sub(magicConstz,magicConstz, moduliProduct); mpz_t bitmaskz; mpz_init(bitmaskz); mpz_ui_pow_ui(bitmaskz, 2, A_bits); mpz_sub_ui(bitmaskz, bitmaskz, 1); #ifdef CRYPTO_DEBUG gmp_printf("Mask used: %Zx\n",bitmaskz); #endif // Shall we prefetch here ? mpz_t tmpz; mpz_init(tmpz); // We need to zero tmpm as export writes nothing on the output for null values bzero(tmpm,polyDegree*nbModuli*sizeof(uint64_t)); for (unsigned int i = 0 ; i < polyDegree ; i++) { //For testing we may do a hardcoded modulus but not always. m[i] = m[i] % modulus; mpz_mul_ui(tmpz, tmprez[i], 2UL); if (mpz_cmp(tmpz, moduliProduct)==1)// tmprez[i] > moduliProduct / 2 { mpz_add(tmpz, tmprez[i], magicConstz); mpz_and(tmprez[i], tmpz, bitmaskz); } else { mpz_and(tmprez[i], tmprez[i], bitmaskz); } // Combien d'uint32 ? int combien = ceil((double)A_bits/32); mpz_export(((uint32_t*)tmpm)+i*combien, NULL, -1, sizeof(uint32_t), 0, 0, tmprez[i]); mpz_clear(tmprez[i]); } delete[] tmprez; mpz_clears(moduliProduct, tmpz, magicConstz, bitmaskz, NULL); } else { // nbModuli=1 // If e*A+m < p/2 we mask the message bits: bitmask = (1ULL< p/2 we do a little trick to avoid signed integers and modulus reduction // e[i]= e[i] + 2**61 - p (we replace p by 2**61) and then bitmask the message. const uint64_t magicConst = (1ULL<<61)-moduli[0];// 2**61 - p // Shall we prefetch here ? for (unsigned int i = 0 ; i < polyDegree ; i++) { //For testing we may do a hardcoded modulus but not always. m[i] = m[i] % modulus; tmpm[i] = (tmpm[i] > moduli[0]/2) ? (tmpm[i] + magicConst)& bitmask : tmpm[i] & bitmask; } } } // MOK is here for the CRT modification // encrypts a uint (e.g. for producing a equest element with a 0 or a 1) // does not return a lwe_cipher but the (char*)pointer on two consecutively allocated poly64 (a and b) char* NFLLWE::encrypt(unsigned int ui, unsigned int d) { if ( ceil(log2(static_cast(ui))) >= publicParams.getAbsorptionBitsize()) { std::cerr << "NFFLWE: The given unsigned int does not fit in " << publicParams.getAbsorptionBitsize() << " bits"<< std::endl; ui %= 1< 1 { nflInstance.serializeData32 ((uint32_t*)clear_data, out_data, bits_per_coordinate, ceil((double)bits_per_coordinate/32)* polyDegree); } #ifdef DEBUG //std::cout<<"Bitgrouped into: "<& crypto_params) { unsigned int params_nbr = 0; unsigned int k_array_size = 5; unsigned int k[5] = {80, 100, 128, 192, 256}; for (unsigned int i = 0 ; i < k_array_size ; i++) { params_nbr += getCryptoParams(k[i], crypto_params); } return params_nbr; } unsigned int NFLLWE::getCryptoParams(unsigned int k, std::set& crypto_params) { using namespace std; unsigned int p_size, params_nbr = 0; for (unsigned int degree = kMinPolyDegree ; degree <= kMaxPolyDegree; degree <<= 1) { string param; p_size = findMaxModulusBitsize(k, degree); for (unsigned int i = 1; i * kModulusBitsize <= p_size && i * kModulusBitsize <= kMaxAggregatedModulusBitsize; i++) { param = cryptoName + ":" + to_string(estimateSecurity(degree,i*kModulusBitsize)) + ":" + to_string(degree) + ":" + to_string(i*kModulusBitsize) ; if (crypto_params.insert(param).second) params_nbr++; param = ""; } } return params_nbr; } void NFLLWE::recomputeNoiseAmplifiers() { uint64_t A_bits= publicParams.getAbsorptionBitsize() / publicParams.getpolyDegree(); mpz_t tmpz1,tmpz2; mpz_init(tmpz1); mpz_init(tmpz2); for(unsigned short currentModulus=0;currentModulus nParameters; vector qParameters; vector nbrBits; string line; int i(0); // Read lines while(getline(estimations,line)){ // Find the two positions of ':' to split the line int posPoint1=line.find(':',0); int posPoint2=line.find(':',posPoint1+1); // Add the n parameter to the vector unsigned int nData=atoi(line.substr(0,posPoint1).c_str()); nParameters.push_back(nData); // Add the q parameter to the vector unsigned int qData=atoi(line.substr(posPoint1+1,posPoint2-(posPoint1+1)).c_str()); qParameters.push_back(qData); // Add the number of bits to the vector unsigned int nbrBitsData=atoi(line.substr(posPoint2+1,line.size()-(posPoint2+1)).c_str()); nbrBits.push_back(nbrBitsData); } // Initialize the estimation at 0 unsigned int estimated_k(0); // Check all the n and q parmaters to find a correspondence with the inputs parameters for(int i(0); i(publicParams.getnoiseUB()); double nb_sum = elt_nbr; double p_size = getmodulusBitsize(); double nbr_bit = floor(( (p_size - 1) - log2(nb_sum) - log2(Berr) -log2(static_cast(polyDegree))) / 2.0); publicParams.setAbsPCBitsize(nbr_bit); recomputeNoiseAmplifiers(); return long(nbr_bit); } unsigned int NFLLWE::findMaxModulusBitsize(unsigned int k, unsigned int n) { unsigned int p_size; //p_size can not be too low p_size = kModulusBitsize; while (!checkParamsSecure(k,n,p_size)) p_size+=kModulusBitsize; return p_size-kModulusBitsize; } bool NFLLWE::checkParamsSecure(unsigned int k, unsigned int n, unsigned int p_size) { return (estimateSecurity(n,p_size)<=k); } double NFLLWE::lllOutput(unsigned int n, double& p, double delta) { double m = 2*n + 128; //execution log(time) = 1.8/ log(delta) − 110 and -80 to compute processor cycles. We add a margin of 20 so we take k/2 = 1.8/log(delta) - 100 double lll1 = pow(delta, m) * pow(p, n/m); double lll2 = 2 * sqrt(n * log2(p) * log2(delta)); lll2 = pow(2, lll2); return std::min(lll1, lll2); } double NFLLWE::estimateAbsTime(std::string crypto_param) { using namespace std; vector fields; boost::algorithm::split(fields, crypto_param, boost::algorithm::is_any_of(":")); unsigned int p_size = (unsigned) atoi(fields[3].c_str()); double a = (p_size < 64) ? 1 : ceil(static_cast(p_size)/64.0); unsigned int degree = (unsigned) atoi(fields[2].c_str()); double b = degree/1024; return 1/(1.75 * pow(10, 5)/(a*b)); } double NFLLWE::estimatePrecomputeTime(std::string crypto_param) { using namespace std; vector fields; boost::algorithm::split(fields, crypto_param, boost::algorithm::is_any_of(":")); unsigned int p_size = (unsigned) atoi(fields[3].c_str()); double a = (p_size < 64) ? 1 : ceil(static_cast(p_size)/64.0); unsigned int degree = (unsigned) atoi(fields[2].c_str()); double b = degree/1024; return 1/(0.75*pow(10, 5)/(a*b)); } unsigned int NFLLWE::getmodulusBitsize() { return nbModuli*kModulusBitsize; } // ********************************************************* // AbstractPublicParameters stuff // ********************************************************* AbstractPublicParameters& NFLLWE::getPublicParameters() { //This was bug chasing but should not be necessary! publicParams.setcrypto_container(this); return publicParams; } std::string NFLLWE::getSerializedCryptoParams(bool shortversion) { return publicParams.getSerializedParams(shortversion); } NFLLWE::~NFLLWE() { clearSecretKeys(); } std::string& NFLLWE::toString() { return cryptoName; } void NFLLWE::clearSecretKeys() { if(oldNbModuli) { // secreKey was allocated with a single allocation delete[] Abit_mod; delete[] Abit_mod_shoup; free(secretKey[0]); delete[] secretKey; } if(oldNbModuli) { for (unsigned int i = 0; i < oldNbModuli; i++) { free(secretKeyShoup[i]); } delete[] secretKeyShoup; } oldNbModuli = 0; } //This main is for benchmarking and tests // // int main(int c,char **v) { // // // Benchs et correctness enc/dec // NFLLWE n; // n.setNewParameters(1024,64,22); // n.setmodulus(P64); // n.getPublicParameters().computeNewParameters("lwe:80:1024:64:22"); // // poly64 p=n.boundedRandomPoly(1024, 1023); // poly64 result=(poly64)calloc(1024,sizeof(uint64_t)); // // std::cout<<"0-RND polynom: ";n.print_poly64(p,4);std::cout<