/* 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
//#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;
string k_str = to_string(k);
for (unsigned int degree = kMinPolyDegree ; degree <= kMaxPolyDegree; degree <<= 1)
{
string param;
p_size = findMaxModulusBitsize(k, degree);
// We give a very small margin 59 instead of 60 so that 100:1024:60 passes the test
//for (unsigned int i = 1; i * 59 <= p_size ; i++)//(p_size > 64) && ((p_size % 64) != 0))
for (unsigned int i = 1; i * 59 <= p_size && i * 60 <= 240; 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(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 = 10;
while (!checkParamsSecure(k,n,p_size)) p_size++;
return --p_size;
}
bool NFLLWE::checkParamsSecure(unsigned int k, unsigned int n, unsigned int p_size)
{
double p, beta, logBerr = 8, epsi, lll;
//We take an advantage of 2**(-k/2) and an attack time of 2**(k/2)
epsi = pow(2, -static_cast(k/2));
//log(time) = 1.8/ log(delta) − 110 and -80 to compute processor cycles so we take pow(2, k/2) = 1.8/log(delta) - 80
double delta = pow(2,1.8/(k/2 + 80));
p = pow(2, p_size) - 1;
beta = (p / logBerr) * sqrt(log1p( 1 / epsi) / M_PI);
lll = lllOutput(n, p, delta);
// We love ugly tricks !
return (lll < beta);// && cout << "beta : " << beta << " p_size : " << p_size << " n :"<< n << " k : "<< k << endl;
}
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<a), (unsigned int)0,(size_t) 0,(size_t) 0);
// // char *charptr;
// // charptr=n.encrypt(1,0);
// // unciphereddata_poly = (poly64)n.decrypt(charptr, (unsigned int)0,(size_t) 0,(size_t) 0);
// // std::cout<<"1-Decoded into polynom: ";n.print_poly64(unciphereddata_poly,1024);std::cout<