static void add1Dmats4dim(FHESecKey& sKey, long i, long keyID) { const FHEcontext &context = sKey.getContext(); long m = context.zMStar.getM(); computeParams(context,m,i); // defines vars: native, ord, gi, g2md, giminv, g2mdminv /* MAUTO vector<long> vals; */ for (long j=1,val=gi; j < ord; j++) { // From s(X^val) to s(X) sKey.GenKeySWmatrix(1, val, keyID, keyID); if (!native) { // also from s(X^{g^{i-ord}}) to s(X) long val2 = MulModPrecon(val,g2md,m,g2mdminv); sKey.GenKeySWmatrix(1, val2, keyID, keyID); /* MAUTO vals.push_back(val2); */ } /* MAUTO vals.push_back(val); */ val = MulModPrecon(val, gi, m, giminv); // val *= g mod m (= g^{j+1}) } if (!native) { sKey.GenKeySWmatrix(1, context.zMStar.genToPow(i, -ord), keyID, keyID); } /* MAUTO sKey.resetTree(i,keyID); // remove existing tree, if any sKey.add2tree(i, 1, vals, keyID); */ }
static void addMinimal1Dmats4dim(FHESecKey& sKey, long i, long keyID) { const PAlgebra& zMStar = sKey.getContext().zMStar; long ord; bool native; if (i != -1) { ord = zMStar.OrderOf(i); native = zMStar.SameOrd(i);\ } else { // Frobenius ord = zMStar.getOrdP(); native = true; } sKey.GenKeySWmatrix(1, zMStar.genToPow(i, 1), keyID, keyID); if (!native) sKey.GenKeySWmatrix(1, zMStar.genToPow(i, -ord), keyID, keyID); if (ord > FHE_KEYSWITCH_MIN_THRESH) { long g = KSGiantStepSize(ord); sKey.GenKeySWmatrix(1, zMStar.genToPow(i, g), keyID, keyID); } sKey.setKSStrategy(i, FHE_KSS_MIN); }
void addTheseMatrices(FHESecKey& sKey, const std::set<long>& automVals, long keyID) { std::set<long>::iterator it; for (it=automVals.begin(); it!=automVals.end(); ++it) { long k = *it; sKey.GenKeySWmatrix(1, k, keyID, keyID); } sKey.setKeySwitchMap(); // re-compute the key-switching map }
// Generate all Frobenius matrices of the form s(X^{p^i})->s(X) void addSomeFrbMatrices(FHESecKey& sKey, long bound, long keyID) { const FHEcontext &context = sKey.getContext(); if (bound >= LONG(context.zMStar.getOrdP())) add1Dmats4dim(sKey, -1, keyID); else // For generators of large order, add only some of the powers addSome1Dmats4dim(sKey, -1, bound, keyID); sKey.setKeySwitchMap(); // re-compute the key-switching map }
void addMinimal1DMatrices(FHESecKey& sKey, long keyID) { const FHEcontext &context = sKey.getContext(); // key-switching matrices for the automorphisms for (long i: range(context.zMStar.numOfGens())) { addMinimal1Dmats4dim(sKey, i, keyID); } sKey.setKeySwitchMap(); // re-compute the key-switching map }
// Generate all Frobenius matrices of the form s(X^{2^i})->s(X) void addFrbMatrices(FHESecKey& sKey, long keyID) { const FHEcontext &context = sKey.getContext(); long m = context.zMStar.getM(); for (long j = 1; j < (long)context.zMStar.getOrdP(); j++) { long val = PowerMod(context.zMStar.getP(), j, m); // val = p^j mod m sKey.GenKeySWmatrix(1, val, keyID, keyID); } sKey.setKeySwitchMap(); // re-compute the key-switching map }
// A maximalistic approach: generate matrices s(X^e)->s(X) for all e \in Zm* void addAllMatrices(FHESecKey& sKey, long keyID) { const FHEcontext &context = sKey.getContext(); long m = context.zMStar.getM(); // key-switching matrices for the automorphisms for (long i = 0; i < m; i++) { if (!context.zMStar.inZmStar(i)) continue; sKey.GenKeySWmatrix(1, i, keyID, keyID); } sKey.setKeySwitchMap(); // re-compute the key-switching map }
// generate only matrices of the form s(X^{g^i})->s(X), but not all of them. // For a generator g whose order is larger than bound, generate only enough // matrices for the giant-step/baby-step procedures (2*sqrt(ord(g))of them). void addSome1DMatrices(FHESecKey& sKey, long bound, long keyID) { const FHEcontext &context = sKey.getContext(); // key-switching matrices for the automorphisms for (long i: range(context.zMStar.numOfGens())) { // For generators of small order, add all the powers if (bound >= context.zMStar.OrderOf(i)) add1Dmats4dim(sKey, i, keyID); else // For generators of large order, add only some of the powers addSome1Dmats4dim(sKey, i, bound, keyID); } sKey.setKeySwitchMap(); // re-compute the key-switching map }
// generate all matrices of the form s(X^{g^i})->s(X) for generators g of // Zm* /<2> and i<ord(g). If g has different orders in Zm* and Zm* /<2> // then generate also matrices of the form s(X^{g^{-i}})->s(X) void add1DMatrices(FHESecKey& sKey, long keyID) { const FHEcontext &context = sKey.getContext(); long m = context.zMStar.getM(); // key-switching matrices for the automorphisms for (long i = 0; i < (long)context.zMStar.numOfGens(); i++) { for (long j = 1; j < (long)context.zMStar.OrderOf(i); j++) { long val = PowerMod(context.zMStar.ZmStarGen(i), j, m); // val = g^j // From s(X^val) to s(X) sKey.GenKeySWmatrix(1, val, keyID, keyID); if (!context.zMStar.SameOrd(i)) // also from s(X^{1/val}) to s(X) sKey.GenKeySWmatrix(1, InvMod(val,m), keyID, keyID); } } sKey.setKeySwitchMap(); // re-compute the key-switching map }
// Generate all key-switching matrices for a given permutation network void addMatrices4Network(FHESecKey& sKey, const PermNetwork& net, long keyID) { const FHEcontext &context = sKey.getContext(); long m = context.zMStar.getM(); for (long i=0; i<net.depth(); i++) { long e = net.getLayer(i).getE(); long gIdx = net.getLayer(i).getGenIdx(); long g = context.zMStar.ZmStarGen(gIdx); long g2e = PowerMod(g, e, m); // g^e mod m const Vec<long>&shamts = net.getLayer(i).getShifts(); for (long j=0; j<shamts.length(); j++) { if (shamts[j]==0) continue; long val = PowerMod(g2e, shamts[j], m); sKey.GenKeySWmatrix(1, val, keyID, keyID); } } sKey.setKeySwitchMap(); // re-compute the key-switching map }
// generate only matrices of the form s(X^{g^i})->s(X), but not all of them. // For a generator g whose order is larger than bound, generate only enough // matrices for the giant-step/baby-step procedures (2*sqrt(ord(g))of them). void addSome1DMatrices(FHESecKey& sKey, long bound, long keyID) { const FHEcontext &context = sKey.getContext(); long m = context.zMStar.getM(); // key-switching matrices for the automorphisms for (long i = 0; i < (long)context.zMStar.numOfGens(); i++) { // For generators of small order, add all the powers if (bound >= (long)context.zMStar.OrderOf(i)) for (long j = 1; j < (long)context.zMStar.OrderOf(i); j++) { long val = PowerMod(context.zMStar.ZmStarGen(i), j, m); // val = g^j // From s(X^val) to s(X) sKey.GenKeySWmatrix(1, val, keyID, keyID); if (!context.zMStar.SameOrd(i)) // also from s(X^{1/val}) to s(X) sKey.GenKeySWmatrix(1, InvMod(val,m), keyID, keyID); } else { // For generators of large order, add only some of the powers long num = SqrRoot(context.zMStar.OrderOf(i)); // floor(ord^{1/2}) if (num*num < (long) context.zMStar.OrderOf(i)) num++; // ceil(ord^{1/2}) // VJS: the above two lines replaces the following inexact calculation // with an exact calculation // long num = ceil(sqrt((double)context.zMStar.OrderOf(i))); for (long j=1; j <= num; j++) { // Add matrices for g^j and g^{j*num} long val1 = PowerMod(context.zMStar.ZmStarGen(i), j, m); // g^j long val2 = PowerMod(context.zMStar.ZmStarGen(i),num*j,m);// g^{j*num} if (j < num) { sKey.GenKeySWmatrix(1, val1, keyID, keyID); sKey.GenKeySWmatrix(1, val2, keyID, keyID); } if (!context.zMStar.SameOrd(i)) { // sKey.GenKeySWmatrix(1, InvMod(val1,m), keyID, keyID); sKey.GenKeySWmatrix(1, InvMod(val2,m), keyID, keyID); } } // VJS: experimantal feature...because the replication code // uses rotations by -1, -2, -4, -8, we add a few // of these as well...only the small ones are important, // and we only need them if SameOrd(i)... // Note: we do indeed get a nontrivial speed-up if (context.zMStar.SameOrd(i)) { for (long k = 1; k <= num; k = 2*k) { long j = context.zMStar.OrderOf(i) - k; long val = PowerMod(context.zMStar.ZmStarGen(i), j, m); // val = g^j sKey.GenKeySWmatrix(1, val, keyID, keyID); } } } } sKey.setKeySwitchMap(); // re-compute the key-switching map }
// same as above, but uses BS/GS strategy static void addSome1Dmats4dim(FHESecKey& sKey, long i, long bound, long keyID) { const PAlgebra& zMStar = sKey.getContext().zMStar; long ord; bool native; if (i != -1) { ord = zMStar.OrderOf(i); native = zMStar.SameOrd(i);\ } else { // Frobenius ord = zMStar.getOrdP(); native = true; } long g = KSGiantStepSize(ord); // baby steps for (long j = 1; j < g; j++) sKey.GenKeySWmatrix(1, zMStar.genToPow(i, j), keyID, keyID); // giant steps for (long j = g; j < ord; j += g) sKey.GenKeySWmatrix(1, zMStar.genToPow(i, j), keyID, keyID); if (!native) sKey.GenKeySWmatrix(1, zMStar.genToPow(i, -ord), keyID, keyID); sKey.setKSStrategy(i, FHE_KSS_BSGS); // NOTE: the old code also added matrices for ord-2^k for small k, // in the case of (native && i<context.zMStar.numOfGens()). // This supposedly speeds up the replication code, but for now // we are leaving this out, for simplicity. Also, it is a waste // of space for applications that don't use replication. }
// adds all matrices for dim i. // i == -1 => Frobenius (NOTE: in matmul1D, i ==#gens means something else, // so it is best to avoid that). static void add1Dmats4dim(FHESecKey& sKey, long i, long keyID) { const PAlgebra& zMStar = sKey.getContext().zMStar; long ord; bool native; if (i != -1) { ord = zMStar.OrderOf(i); native = zMStar.SameOrd(i);\ } else { // Frobenius ord = zMStar.getOrdP(); native = true; } for (long j = 1; j < ord; j++) sKey.GenKeySWmatrix(1, zMStar.genToPow(i, j), keyID, keyID); if (!native) sKey.GenKeySWmatrix(1, zMStar.genToPow(i, -ord), keyID, keyID); sKey.setKSStrategy(i, FHE_KSS_FULL); }
void decryptAndPrint(ostream& s, const Ctxt& ctxt, const FHESecKey& sk, const EncryptedArray& ea, long flags) { const FHEcontext& context = ctxt.getContext(); xdouble noiseEst = sqrt(ctxt.getNoiseVar()); xdouble modulus = xexp(context.logOfProduct(ctxt.getPrimeSet())); vector<ZZX> ptxt; ZZX p, pp; sk.Decrypt(p, ctxt, pp); s << "plaintext space mod "<<ctxt.getPtxtSpace() << ", level="<<ctxt.findBaseLevel() << ", \n |noise|=q*" << (coeffsL2Norm(pp)/modulus) << ", |noiseEst|=q*" << (noiseEst/modulus) <<endl; if (flags & FLAG_PRINT_ZZX) { s << " before mod-p reduction="; printZZX(s,pp) <<endl; } if (flags & FLAG_PRINT_POLY) { s << " after mod-p reduction="; printZZX(s,p) <<endl; } if (flags & FLAG_PRINT_VEC) { ea.decode(ptxt, p); if (ea.getAlMod().getTag() == PA_zz_p_tag && ctxt.getPtxtSpace() != ea.getAlMod().getPPowR()) { long g = GCD(ctxt.getPtxtSpace(), ea.getAlMod().getPPowR()); for (long i=0; i<ea.size(); i++) PolyRed(ptxt[i], g, true); } s << " decoded to "; if (deg(p) < 40) // just pring the whole thing s << ptxt << endl; else if (ptxt.size()==1) // a single slot printZZX(s, ptxt[0]) <<endl; else { // print first and last slots printZZX(s, ptxt[0],20) << "--"; printZZX(s, ptxt[ptxt.size()-1], 20) <<endl; } } }
void checkCiphertext(const Ctxt& ctxt, const ZZX& ptxt, const FHESecKey& sk) { const FHEcontext& context = ctxt.getContext(); /* IndexSet base = baseSetOf(ctxt); double addedNoise = log(ctxt.modSwitchAddedNoiseVar()); Ctxt tmp = ctxt; tmp.modDownToSet(base); double totalNoise = log(tmp.getNoiseVar()); cout << " @@@ log(added-noise)="<<addedNoise << ", log(total-noise)="<<totalNoise<<endl; */ cout << " ln(q)="<< context.logOfProduct(ctxt.getPrimeSet()) << ", ln(nVar)/2="<< log(ctxt.getNoiseVar())/2; // << ", ln(nMag)="<< log(ctxt.getNoiseMag()); ZZX res; // sk.Decrypt(res, ctxt); ZZX f; sk.Decrypt(res, ctxt, f); cout << ", ln(mxPtxtCoef)=" << log(largestCoeff(f)); // ensure we reduce the same way on both PolyRed((ZZX&)res,res,ctxt.getPtxtSpace(),true); PolyRed((ZZX&)ptxt,ptxt,ctxt.getPtxtSpace(),true); if (res != ptxt) { cout << ", failed\n"; for (long i=0; i<=deg(ptxt); i++) if (coeff(res,i)!=coeff(ptxt,i)) { cout << "first mismatch in coeff "<<i<<": " << coeff(res,i)<<"!="<<coeff(ptxt,i)<<"\n"; break; } cout << "Timing information:\n"; printAllTimers(); cout << "\n"; exit(0); } else cout << ", succeeded\n"; }
static void addSome1Dmats4dim(FHESecKey& sKey, long i, long bound, long keyID) { const FHEcontext &context = sKey.getContext(); long m = context.zMStar.getM(); computeParams(context,m,i); // defines vars: native, ord, gi, g2md, giminv, g2mdminv long baby, giant; std::tie(baby,giant) = computeSteps(ord, bound, native); for (long j=1,val=gi; j<=baby; j++) { // Add matrices for baby steps sKey.GenKeySWmatrix(1, val, keyID, keyID); if (!native) { long val2 = MulModPrecon(val,g2md,m,g2mdminv); sKey.GenKeySWmatrix(1, val2, keyID, keyID); } val = MulModPrecon(val, gi, m, giminv); // val *= g mod m (= g^{j+1}) } long gb = PowerMod(gi,baby,m); // g^baby NTL::mulmod_precon_t gbminv = PrepMulModPrecon(gb, m); for (long j=2,val=gb; j < giant; j++) { // Add matrices for giant steps val = MulModPrecon(val, gb, m, gbminv); // val = g^{(j+1)*baby} sKey.GenKeySWmatrix(1, val, keyID, keyID); } if (!native) { sKey.GenKeySWmatrix(1, context.zMStar.genToPow(i, -ord), keyID, keyID); } // VJS: experimantal feature...because the replication code // uses rotations by -1, -2, -4, -8, we add a few // of these as well...only the small ones are important, // and we only need them if SameOrd(i)... // Note: we do indeed get a nontrivial speed-up if (native && i<context.zMStar.numOfGens()) { for (long k = 1; k < giant; k = 2*k) { long j = ord - k; long val = PowerMod(gi, j, m); // val = g^j sKey.GenKeySWmatrix(1, val, keyID, keyID); } } #if 0 MAUTO // build the tree for this dimension, the internal nodes are 1 and // (subset of) gi^{giant}, gi^{2*giant}, ..., gi^{baby*giant}. We MAUTO sKey.resetTree(i,keyID); // remove existing tree, if any // keep a list of all the elements that are covered by the tree so far, // initialized to only the root (=1). std::unordered_set<long> covered({1}); // Make a list of the automorphisms for this dimension std::vector<long> autos; for (long j=1,val=gi; j<ord; j++) { // Do we have matrices for val and/or val/gi^{di}? if (!native) { long val2 = MulModPrecon(val, g2md, m, g2mdminv); if (sKey.haveKeySWmatrix(1,val2,keyID,keyID)) { autos.push_back(val2); } } if (sKey.haveKeySWmatrix(1,val,keyID,keyID)) { autos.push_back(val); } val = MulModPrecon(val, gi, m, giminv); // g^{j+1} } // Insert internal nodes and their children to tree for (long j=0,fromVal=1; j<giant; j++) { NTL::mulmod_precon_t fromminv = PrepMulModPrecon(fromVal, m); vector<long> children; for (long k: autos) { long toVal = MulModPrecon(k, fromVal, m, fromminv); if (covered.count(toVal)==0) { // toVal not covered yet covered.insert(toVal); children.push_back(toVal); } } if (!children.empty()) { // insert fromVal with its children sKey.add2tree(i, fromVal, children, keyID); } fromVal = MulModPrecon(fromVal, gb, m, gbminv); // g^{(j+1)*baby} } // Sanity-check, did we cover everything? long toCover = native? ord: (2*ord-1); if (covered.size()<toCover) cerr << "**Warning: order-"<<ord<<" dimension, covered "<<covered.size() << " of "<<toCover<<endl; #endif }
// Generate all Frobenius matrices of the form s(X^{p^i})->s(X) void addMinimalFrbMatrices(FHESecKey& sKey, long keyID) { addMinimal1Dmats4dim(sKey, -1, keyID); sKey.setKeySwitchMap(); // re-compute the key-switching map }