#include "FHE/NTL-Subs.h" #include "Math/Setup.h" #include "Math/gfp.h" #include "Math/gf2n.h" #include "FHE/P2Data.h" #include "FHE/QGroup.h" #include "FHE/NoiseBounds.h" #include "Tools/mkpath.h" #include "FHEOffline/Proof.h" #include using namespace std; #include #include #include #include NTL_CLIENT #include "FHEOffline/DataSetup.h" template <> void generate_setup(int n_parties, int plaintext_length, int sec, FHE_Params& params, FFT_Data& FTD, int slack, bool round_up) { Ring Rp; bigint p0p,p1p,p; SPDZ_Data_Setup_Char_p(Rp, FTD, p0p, p1p, n_parties, plaintext_length, sec, slack, round_up); params.set(Rp, {p0p, p1p}); } template <> void generate_setup(int n_parties, int plaintext_length, int sec, FHE_Params& params, P2Data& P2D, int slack, bool round_up) { Ring R; bigint pr0,pr1; SPDZ_Data_Setup_Char_2(R, P2D, pr0, pr1, n_parties, plaintext_length, sec, slack, round_up); params.set(R, {pr0, pr1}); } void generate_setup(int n, int lgp, int lg2, int sec, bool skip_2, int slack, bool round_up) { DataSetup setup; // do the full setup for SHE data generate_setup(n, lgp, sec, setup.setup_p.params, setup.setup_p.FieldD, slack, round_up); if (!skip_2) generate_setup(n, lg2, sec, setup.setup_2.params, setup.setup_2.FieldD, slack, round_up); setup.write_setup(skip_2); } bool same_word_length(int l1, int l2) { return l1 / 64 == l2 / 64; } template <> int generate_semi_setup(int plaintext_length, int sec, FHE_Params& params, FFT_Data& FTD, bool round_up) { int m = 1024; int lgp = plaintext_length; bigint p; generate_prime(p, lgp, m); int lgp0, lgp1; while (true) { SemiHomomorphicNoiseBounds nb(p, phi_N(m), 1, sec, numBits(NonInteractiveProof::slack(sec, phi_N(m))), true); bigint p1 = 2 * p * m, p0 = p; while (nb.min_p0(params.n_mults() > 0, p1) > p0) { p0 *= 2; } if (phi_N(m) < nb.min_phi_m(numBits(p0 * (params.n_mults() > 0 ? p1 : 1)))) { m *= 2; generate_prime(p, lgp, m); } else { lgp0 = numBits(p0) + 1; lgp1 = numBits(p1) + 1; break; } } int extra_slack = common_semi_setup(params, m, p, lgp0, lgp1, round_up); FTD.init(params.get_ring(), p); gfp::init_field(p); return extra_slack; } template <> int generate_semi_setup(int plaintext_length, int sec, FHE_Params& params, P2Data& P2D, bool round_up) { if (params.n_mults() > 0) throw runtime_error("only implemented for 0-level BGV"); gf2n_short::init_field(plaintext_length); int m; char_2_dimension(m, plaintext_length); SemiHomomorphicNoiseBounds nb(2, phi_N(m), 1, sec, numBits(NonInteractiveProof::slack(sec, phi_N(m))), true); int lgp0 = numBits(nb.min_p0(false, 0)); int extra_slack = common_semi_setup(params, m, 2, lgp0, -1, round_up); load_or_generate(P2D, params.get_ring()); return extra_slack; } int common_semi_setup(FHE_Params& params, int m, bigint p, int lgp0, int lgp1, bool round_up) { cout << "Need ciphertext modulus of length " << lgp0; if (params.n_mults() > 0) cout << "+" << lgp1; cout << " and " << phi_N(m) << " slots" << endl; int extra_slack = 0; if (round_up) { int i; for (i = 0; i <= 20; i++) { if (SemiHomomorphicNoiseBounds::min_phi_m(lgp0 + i) > phi_N(m)) break; if (not same_word_length(lgp0, lgp0 + i)) break; } extra_slack = i - 1; lgp0 += extra_slack; cout << "Rounding up to " << lgp0 << ", giving extra slack of " << extra_slack << " bits" << endl; } Ring R; ::init(R, m); bigint p0, p1 = 1; if (params.n_mults() > 0) { generate_moduli(p0, p1, m, p, lgp0, lgp1); params.set(R, {p0, p1}); } else { generate_modulus(p0, m, p, lgp0); params.set(R, {p0}); } return extra_slack; } int finalize_lengths(int& lg2p0, int& lg2p1, int n, int m, int* lg2pi, bool round_up) { if (n >= 2 and n <= 10) cout << "Difference to suggestion for p0: " << lg2p0 - lg2pi[n - 2] << ", for p1: " << lg2p1 - lg2pi[9 + n - 2] << endl; cout << "p0 needs " << int(ceil(1. * lg2p0 / 64)) << " words" << endl; cout << "p1 needs " << int(ceil(1. * lg2p1 / 64)) << " words" << endl; int extra_slack = 0; if (round_up) { int i = 0; for (i = 0; i < 10; i++) { if (phi_N(m) < NoiseBounds::min_phi_m(lg2p0 + lg2p1 + 2 * i)) break; if (not same_word_length(lg2p0 + i, lg2p0)) break; if (not same_word_length(lg2p1 + i, lg2p1)) break; } i--; extra_slack = 2 * i; lg2p0 += i; lg2p1 += i; cout << "Rounding up to " << lg2p0 << "+" << lg2p1 << ", giving extra slack of " << extra_slack << " bits" << endl; } cout << "Total length: " << lg2p0 + lg2p1 << endl; return extra_slack; } /****************************************************************************** * Here onwards needs NTL ******************************************************************************/ /* * Subroutine for creating the FHE parameters */ int SPDZ_Data_Setup_Char_p_Sub(Ring& R, bigint& pr0, bigint& pr1, int n, int idx, int& m, bigint& p, int sec, int slack = 0, bool round_up = false) { int lg2pi[5][2][9] = { { {130,132,132,132,132,132,132,132,132}, {104,104,104,106,106,108,108,110,110} }, { {196,196,196,196,198,198,198,198,198}, {136,138,140,142,140,140,140,142,142} }, { {325,325,325,325,330,330,330,330,330}, {205,205,205,210,205,205,205,205,210} }, { {580,585,585,585,585,585,585,585,585}, {330,330,330,335,335,335,335,335,335} }, { {1095,1095,1095,1095,1095,1095,1095,1095,1095}, {590,590,590,590,590,595,595,595,595} } }; int lg2p0 = 0, lg2p1 = 0; if (n >= 2 and n <= 10) { lg2p0=lg2pi[idx][0][n-2]; lg2p1=lg2pi[idx][1][n-2]; } else if (sec == -1) throw runtime_error("no precomputed parameters available"); while (sec != -1) { double phi_m_bound = NoiseBounds(p, phi_N(m), n, sec, slack).optimize(lg2p0, lg2p1); cout << "Trying primes of length " << lg2p0 << " and " << lg2p1 << endl; if (phi_N(m) < phi_m_bound) { int old_m = m; m = 2 << int(ceil(log2(phi_m_bound))); cout << "m = " << old_m << " too small, increasing it to " << m << endl; generate_prime(p, numBits(p), m); } else break; } init(R,m); int extra_slack = finalize_lengths(lg2p0, lg2p1, n, m, lg2pi[idx][0], round_up); generate_moduli(pr0, pr1, m, p, lg2p0, lg2p1); return extra_slack; } void generate_moduli(bigint& pr0, bigint& pr1, const int m, const bigint p, const int lg2p0, const int lg2p1) { generate_modulus(pr0, m, p, lg2p0, "0"); generate_modulus(pr1, m, p, lg2p1, "1", pr0); } void generate_modulus(bigint& pr, const int m, const bigint p, const int lg2pr, const string& i, const bigint& pr0) { int ex; if (lg2pr==0) { throw invalid_params(); } bigint step=m; bigint twop=1<<(numBits(m)+1); bigint gc=gcd(step,twop); step=step*twop/gc; ex=lg2pr-numBits(p)-numBits(step)+1; if (ex<0) { cout << "Something wrong in lg2p" << i << " = " << lg2pr << endl; abort(); } pr=1; pr=(pr< Fi(Gord); vector Rts(Gord); vector u(Gord); /* Find map from Type 0 (mod G) -> Type 1 (mod Fi) for the first of the Fi's only */ Fi[0]=facts[0]; GF2E::init(facts[0]); // work with the extension field GF_2[X]/Fi[0] GF2EX Ga=to_GF2EX(G); // represent G as a polynomial over the extension field Rts[0]=rep(FindRoot(Ga)); // Find a roof of G in this field cout << "Fixing field ordering and the maps (Need to count to " << Gord << " here)\n\t"; GF2E::init(G); GF2X g; vector used(facts.length()); for (int i=0; i10) and sec == -1) { throw invalid_params(); } int m,lg2p0,lg2p1,ex; char_2_dimension(m, lg2); if (sec == -1) { lg2p0=lg2pi[0][n-2]; lg2p1=lg2pi[1][n-2]; } else { NoiseBounds(2, phi_N(m), n, sec, slack).optimize(lg2p0, lg2p1); finalize_lengths(lg2p0, lg2p1, n, m, lg2pi[0], round_up); } if (NoiseBounds::min_phi_m(lg2p0 + lg2p1) > phi_N(m)) throw runtime_error("number of slots too small"); cout << "m = " << m << endl; init(R,m); if (lg2p0==0 || lg2p1==0) { throw invalid_params(); } // We want pr0=pr1=1 mod (m*twop) where twop is the smallest // power of two bigger than 2*m. This means we have m'th roots of // unity and twop'th roots of unity. This means FFT's are easier // to implement int lg2m=numBits(m); bigint step=m<<(lg2m+1); ex=lg2p0-2*lg2m; pr0=1; pr0=(pr0< primes(100),exp(100); PrimeSeq s; long pr; pr=s.next(); int len=0; while (pr<2*mm) { int e=0; while ((t%pr)==0) { e++; t=t/pr; } if (e!=0) { primes[len]=pr; exp[len]=e; if (len!=0) { cout << " * "; } cout << pr << "^" << e << flush; len++; } pr=s.next(); } cout << endl; // We want to find the best m which divides pr-1, such that // - 2*m > phi(m) > mm // - m has the smallest number of factors vector ee; ee.resize(len); for (int i=0; imx) { mx=ee[i]; } } } // Put "if" here to stop searching for things which will never work if (cand_m>1 && cand_m<4*mm) { //cout << " : " << cand_m << " : " << hwt << flush; int phim=phi_N(cand_m); //cout << " : " << phim << " : " << mm << endl; if (phim>mm && phim<3*mm) { if (m==-1 || hwtexp[i] && flag) { ee[i]=0; i++; if (i==len) { flag=false; i=0; } else { ee[i]=ee[i]+1; } } } if (m==-1) { throw bad_value(); } cout << "Chosen value of m=" << m << "\t\t phi(m)=" << bphi_m << " : " << min_hwt << " : " << bmx << endl; SPDZ_Data_Setup_Char_p_Sub(R,pr0,pr1,n,idx,m,p,sec); int mx=0; for (int i=0; i