/************** Each round consists of the following: 1. c1.multiplyBy(c0) 2. c0 += random constant 3. c2 *= random constant 4. tmp = c1 5. ea.rotate(tmp, random amount in [-nSlots/2, nSlots/2]) 6. c2 += tmp 7. ea.rotate(c2, random amount in [1-nSlots, nSlots-1]) 8. c1.negate() 9. c3.multiplyBy(c2) 10. c0 -= c3 **************/ void testGeneralOps(const FHEPubKey& publicKey, const FHESecKey& secretKey, const EncryptedArrayCx& ea, double epsilon, long nRounds) { long nslots = ea.size(); char buffer[32]; vector<cx_double> p0, p1, p2, p3; ea.random(p0); ea.random(p1); ea.random(p2); ea.random(p3); Ctxt c0(publicKey), c1(publicKey), c2(publicKey), c3(publicKey); ea.encrypt(c0, publicKey, p0, /*size=*/1.0); ea.encrypt(c1, publicKey, p1, /*size=*/1.0); ea.encrypt(c2, publicKey, p2, /*size=*/1.0); ea.encrypt(c3, publicKey, p3, /*size=*/1.0); resetAllTimers(); FHE_NTIMER_START(Circuit); for (long i = 0; i < nRounds; i++) { if (verbose) std::cout << "*** round " << i << "..."<<endl; long shamt = RandomBnd(2*(nslots/2) + 1) - (nslots/2); // random number in [-nslots/2..nslots/2] long rotamt = RandomBnd(2*nslots - 1) - (nslots - 1); // random number in [-(nslots-1)..nslots-1] // two random constants vector<cx_double> const1, const2; ea.random(const1); ea.random(const2); ZZX const1_poly, const2_poly; ea.encode(const1_poly, const1, /*size=*/1.0); ea.encode(const2_poly, const2, /*size=*/1.0); mul(p1, p0); // c1.multiplyBy(c0) c1.multiplyBy(c0); if (verbose) { CheckCtxt(c1, "c1*=c0"); debugCompare(ea, secretKey, p1, c1, epsilon); } add(p0, const1); // c0 += random constant c0.addConstant(const1_poly); if (verbose) { CheckCtxt(c0, "c0+=k1"); debugCompare(ea, secretKey, p0, c0, epsilon); } mul(p2, const2); // c2 *= random constant c2.multByConstant(const2_poly); if (verbose) { CheckCtxt(c2, "c2*=k2"); debugCompare(ea, secretKey, p2, c2, epsilon); } vector<cx_double> tmp_p(p1); // tmp = c1 Ctxt tmp(c1); sprintf(buffer, "tmp=c1>>=%d", (int)shamt); rotate(tmp_p, shamt); // ea.shift(tmp, random amount in [-nSlots/2,nSlots/2]) ea.rotate(tmp, shamt); if (verbose) { CheckCtxt(tmp, buffer); debugCompare(ea, secretKey, tmp_p, tmp, epsilon); } add(p2, tmp_p); // c2 += tmp c2 += tmp; if (verbose) { CheckCtxt(c2, "c2+=tmp"); debugCompare(ea, secretKey, p2, c2, epsilon); } sprintf(buffer, "c2>>>=%d", (int)rotamt); rotate(p2, rotamt); // ea.rotate(c2, random amount in [1-nSlots, nSlots-1]) ea.rotate(c2, rotamt); if (verbose) { CheckCtxt(c2, buffer); debugCompare(ea, secretKey, p2, c2, epsilon); } negateVec(p1); // c1.negate() c1.negate(); if (verbose) { CheckCtxt(c1, "c1=-c1"); debugCompare(ea, secretKey, p1, c1, epsilon); } mul(p3, p2); // c3.multiplyBy(c2) c3.multiplyBy(c2); if (verbose) { CheckCtxt(c3, "c3*=c2"); debugCompare(ea, secretKey, p3, c3, epsilon); } sub(p0, p3); // c0 -= c3 c0 -= c3; if (verbose) { CheckCtxt(c0, "c0=-c3"); debugCompare(ea, secretKey, p0, c0, epsilon); } } c0.cleanUp(); c1.cleanUp(); c2.cleanUp(); c3.cleanUp(); FHE_NTIMER_STOP(Circuit); vector<cx_double> pp0, pp1, pp2, pp3; ea.decrypt(c0, secretKey, pp0); ea.decrypt(c1, secretKey, pp1); ea.decrypt(c2, secretKey, pp2); ea.decrypt(c3, secretKey, pp3); std::cout << "Test "<<nRounds<<" rounds of mixed operations, "; if (cx_equals(pp0, p0,conv<double>(epsilon*c0.getPtxtMag())) && cx_equals(pp1, p1,conv<double>(epsilon*c1.getPtxtMag())) && cx_equals(pp2, p2,conv<double>(epsilon*c2.getPtxtMag())) && cx_equals(pp3, p3,conv<double>(epsilon*c3.getPtxtMag()))) std::cout << "PASS\n\n"; else { std::cout << "FAIL:\n"; std::cout << " max(p0)="<<largestCoeff(p0) << ", max(pp0)="<<largestCoeff(pp0) << ", maxDiff="<<calcMaxDiff(p0,pp0) << endl; std::cout << " max(p1)="<<largestCoeff(p1) << ", max(pp1)="<<largestCoeff(pp1) << ", maxDiff="<<calcMaxDiff(p1,pp1) << endl; std::cout << " max(p2)="<<largestCoeff(p2) << ", max(pp2)="<<largestCoeff(pp2) << ", maxDiff="<<calcMaxDiff(p2,pp2) << endl; std::cout << " max(p3)="<<largestCoeff(p3) << ", max(pp3)="<<largestCoeff(pp3) << ", maxDiff="<<calcMaxDiff(p3,pp3) << endl<<endl; } if (verbose) { std::cout << endl; printAllTimers(); std::cout << endl; } resetAllTimers(); }
// recursiveReplicateDim: // d = dimension // ea.sizeOfDimension(d)/2 <= extent <= ea.sizeOfDimension(d), // only positions [0..extent) are non-zero // 1 <= 2^k <= extent: size of current interval // 0 <= pos < ea.sizeOfDimension(d): relative position of first vector // 0 <= limit < ea.sizeOfDimension(): max # of positions to process // dimProd: product of dimensions 0..d // recBound: recursion bound (controls noise) // // SHAI: limit and extent are always the same, it seems static void recursiveReplicateDim(const EncryptedArray& ea, const Ctxt& ctxt, long d, long extent, long k, long pos, long limit, long dimProd, long recBound, RepAuxDim& repAux, ReplicateHandler *handler) { if (pos >= limit) return; if (replicateVerboseFlag) { // DEBUG code cerr << "check: " << k; CheckCtxt(ctxt, ""); } long dSize = ea.sizeOfDimension(d); long nSlots = ea.size(); if (k == 0) { // last level in this dimension: blocks of size 2^k=1 if ( extent >= dSize) { // nothing to do in this dimension replicateAllNextDim(ea, ctxt, d+1, dimProd, recBound, repAux, handler); return; } // SHAI: Will we ever have extent > dSize?? // need to replicate to fill positions [ (1L << n) .. dSize-1 ] if (repAux.tab(d,0).null()) { // generate mask if not there already ZZX mask; SelectRangeDim(ea, mask, 0, dSize - extent, d); repAux.tab(d, 0).set_ptr(new DoubleCRT(mask, ea.getContext())); } Ctxt ctxt_tmp = ctxt; ctxt_tmp.multByConstant(*repAux.tab(d, 0)); ea.rotate1D(ctxt_tmp, d, extent, /*don't-care-flag=*/true); ctxt_tmp += ctxt; replicateAllNextDim(ea, ctxt_tmp, d+1, dimProd, recBound, repAux, handler); return; } // If we need to stop early, call the handler if (handler->earlyStop(d, k, dimProd)) { handler->handle(ctxt); return; } k--; Ctxt ctxt_masked = ctxt; { // artificial scope to miminize storage in the recursion { // another artificial scope (SHAI: this seems redundant) // generate mask at index k+1, if not there yet if (repAux.tab(d, k+1).null()) { // need to generate vector< long > maskArray(nSlots,0); for (long i = 0; i < nSlots; i++) { long c = ea.coordinate(d, i); if (c < extent && bit(c, k) == 0) maskArray[i] = 1; } // store this mask in the repAux table ZZX mask; ea.encode(mask, maskArray); repAux.tab(d, k+1).set_ptr(new DoubleCRT(mask, ea.getContext())); } // Apply mask to zero out slots in ctxt ctxt_masked.multByConstant(*repAux.tab(d, k+1)); } Ctxt ctxt_left = ctxt_masked; ea.rotate1D(ctxt_left, d, 1L << k, /*don't-care-flag=*/true); ctxt_left += ctxt_masked; recursiveReplicateDim(ea, ctxt_left, d, extent, k, pos, limit, dimProd, recBound, repAux, handler); } pos += (1L << k); if (pos >= limit) return; Ctxt ctxt_right = ctxt; ctxt_right -= ctxt_masked; ctxt_masked = ctxt_right; // reuse ctxt_masked as a temp ea.rotate1D(ctxt_masked, d, -(1L << k), /*don't-care-flag=*/true); ctxt_right += ctxt_masked; recursiveReplicateDim(ea, ctxt_right, d, extent, k, pos, limit, dimProd, recBound, repAux, handler); }
void TestIt(long idx, long p, long r, long L, long c, long skHwt, int build_cache=0) { Vec<long> mvec; vector<long> gens; vector<long> ords; long phim = mValues[idx][1]; long m = mValues[idx][2]; assert(GCD(p, m) == 1); append(mvec, mValues[idx][4]); if (mValues[idx][5]>1) append(mvec, mValues[idx][5]); if (mValues[idx][6]>1) append(mvec, mValues[idx][6]); gens.push_back(mValues[idx][7]); if (mValues[idx][8]>1) gens.push_back(mValues[idx][8]); if (mValues[idx][9]>1) gens.push_back(mValues[idx][9]); ords.push_back(mValues[idx][10]); if (abs(mValues[idx][11])>1) ords.push_back(mValues[idx][11]); if (abs(mValues[idx][12])>1) ords.push_back(mValues[idx][12]); if (!noPrint) { cout << "*** TestIt"; if (isDryRun()) cout << " (dry run)"; cout << ": p=" << p << ", r=" << r << ", L=" << L << ", t=" << skHwt << ", c=" << c << ", m=" << m << " (=" << mvec << "), gens="<<gens<<", ords="<<ords << endl; cout << "Computing key-independent tables..." << std::flush; } setTimersOn(); setDryRun(false); // Need to get a "real context" to test bootstrapping double t = -GetTime(); FHEcontext context(m, p, r, gens, ords); if (scale) { context.scale = scale; } context.zMStar.set_cM(mValues[idx][13]/100.0); buildModChain(context, L, c, /*willBeBootstrappable=*/true); if (!noPrint) { std::cout << "security=" << context.securityLevel()<<endl; std::cout << "# small primes = " << context.smallPrimes.card() << "\n"; std::cout << "# ctxt primes = " << context.ctxtPrimes.card() << "\n"; std::cout << "# bits in ctxt primes = " << long(context.logOfProduct(context.ctxtPrimes)/log(2.0) + 0.5) << "\n"; std::cout << "# special primes = " << context.specialPrimes.card() << "\n"; std::cout << "# bits in special primes = " << long(context.logOfProduct(context.specialPrimes)/log(2.0) + 0.5) << "\n"; std::cout << "scale=" << context.scale<<endl; } context.makeBootstrappable(mvec,/*t=*/skHwt,build_cache,/*alsoThick=*/false); // save time...disable some fat boot precomputation t += GetTime(); //if (skHwt>0) context.rcData.skHwt = skHwt; if (!noPrint) { cout << " done in "<<t<<" seconds\n"; cout << " e=" << context.rcData.e << ", e'=" << context.rcData.ePrime << ", a="<< context.rcData.a << ", t=" << context.rcData.skHwt << "\n "; context.zMStar.printout(); } setDryRun(dry); // Now we can set the dry-run flag if desired long p2r = context.alMod.getPPowR(); for (long numkey=0; numkey<OUTER_REP; numkey++) { // test with 3 keys t = -GetTime(); if (!noPrint) cout << "Generating keys, " << std::flush; FHESecKey secretKey(context); secretKey.GenSecKey(64); // A Hamming-weight-64 secret key addSome1DMatrices(secretKey); // compute key-switching matrices that we need addFrbMatrices(secretKey); if (!noPrint) cout << "computing key-dependent tables..." << std::flush; secretKey.genRecryptData(); t += GetTime(); if (!noPrint) cout << " done in "<<t<<" seconds\n"; FHEPubKey publicKey = secretKey; long d = context.zMStar.getOrdP(); long phim = context.zMStar.getPhiM(); long nslots = phim/d; // GG defines the plaintext space Z_p[X]/GG(X) ZZX GG; GG = context.alMod.getFactorsOverZZ()[0]; EncryptedArray ea(context, GG); if (debug) { dbgKey = &secretKey; dbgEa = &ea; } zz_p::init(p2r); Vec<zz_p> val0(INIT_SIZE, nslots); for (auto& x: val0) random(x); vector<ZZX> val1; val1.resize(nslots); for (long i = 0; i < nslots; i++) { val1[i] = conv<ZZX>(conv<ZZ>(rep(val0[i]))); } vector<ZZX> val_const1; val_const1.resize(nslots); for (long i = 0; i < nslots; i++) { val_const1[i] = 1; } Ctxt c1(publicKey); ea.encrypt(c1, publicKey, val1); Ctxt c2(c1); if (!noPrint) CheckCtxt(c2, "before recryption"); publicKey.thinReCrypt(c2); if (!noPrint) CheckCtxt(c2, "after recryption"); vector<ZZX> val2; ea.decrypt(c2, secretKey, val2); if (val1 == val2) cout << "GOOD\n"; else cout << "BAD\n"; } }
static void recursiveReplicate(const EncryptedArray& ea, const Ctxt& ctxt, long n, long k, long pos, long limit, RepAux& repAux, ReplicateHandler *handler) { if (pos >= limit) return; if (replicateVerboseFlag) { // DEBUG code cerr << "check: " << k; CheckCtxt(ctxt, ""); } long nSlots = ea.size(); if (k == 0) { if ( (1L << n) >= nSlots) { handler->handle(ctxt); return; } // need to replicate to fill positions [ (1L << n) .. nSlots ) if (repAux.tab(0).null()) { // need to generate mask ZZX mask; SelectRange(ea, mask, 0, nSlots - (1L << n)); repAux.tab(0).set_ptr(new DoubleCRT(mask, ea.getContext())); } Ctxt ctxt_tmp = ctxt; ctxt_tmp.multByConstant(*repAux.tab(0)); ea.rotate(ctxt_tmp, 1L << n); ctxt_tmp += ctxt; handler->handle(ctxt_tmp); return; } k--; Ctxt ctxt_masked = ctxt; { // artificial scope to miminize storage in // the recursion { // another artificial scope // mask should be at index k+1 if (repAux.tab(k+1).null()) { // need to generate mask vector< long > maskArray; maskArray.resize(nSlots); for (long i = 0; i < (1L << n); i++) maskArray[i] = 1- bit(i, k); // the reverse of bit k of i for (long i = (1L << n); i < nSlots; i++) maskArray[i] = 0; ZZX mask; ea.encode(mask, maskArray); repAux.tab(k+1).set_ptr(new DoubleCRT(mask, ea.getContext())); } ctxt_masked.multByConstant(*repAux.tab(k+1)); } Ctxt ctxt_left = ctxt_masked; ea.rotate(ctxt_left, 1L << k); ctxt_left += ctxt_masked; recursiveReplicate(ea, ctxt_left, n, k, pos, limit, repAux, handler); } pos += (1L << k); if (pos >= limit) return; Ctxt ctxt_right = ctxt; ctxt_right -= ctxt_masked; ctxt_masked = ctxt_right; // reuse ctxt_masked as a temp ea.rotate(ctxt_masked, -(1L << k)); ctxt_right += ctxt_masked; recursiveReplicate(ea, ctxt_right, n, k, pos, limit, repAux, handler); }
// bootstrap a ciphertext to reduce noise void FHEPubKey::thinReCrypt(Ctxt &ctxt) { FHE_TIMER_START; // Some sanity checks for dummy ciphertext long ptxtSpace = ctxt.getPtxtSpace(); if (ctxt.isEmpty()) return; if (ctxt.parts.size()==1 && ctxt.parts[0].skHandle.isOne()) { // Dummy encryption, just ensure that it is reduced mod p ZZX poly = to_ZZX(ctxt.parts[0]); for (long i=0; i<poly.rep.length(); i++) poly[i] = to_ZZ( rem(poly[i],ptxtSpace) ); poly.normalize(); ctxt.DummyEncrypt(poly); return; } assert(recryptKeyID>=0); // check that we have bootstrapping data long p = ctxt.getContext().zMStar.getP(); long r = ctxt.getContext().alMod.getR(); long p2r = ctxt.getContext().alMod.getPPowR(); const ThinRecryptData& trcData = ctxt.getContext().trcData; // the bootstrapping key is encrypted relative to plaintext space p^{e-e'+r}. long e = trcData.e; long ePrime = trcData.ePrime; long p2ePrime = power_long(p,ePrime); long q = power_long(p,e)+1; assert(e>=r); #ifdef DEBUG_PRINTOUT cerr << "reCrypt: p="<<p<<", r="<<r<<", e="<<e<<" ePrime="<<ePrime << ", q="<<q<<endl; #endif // can only bootstrap ciphertext with plaintext-space dividing p^r assert(p2r % ptxtSpace == 0); #ifdef PRINT_LEVELS CheckCtxt(ctxt, "init"); #endif if (thinRecrypt_initial_level) { // experimental code...we should drop down // to a reasonably small level before doing the // first linear map. // TODO: we should also check that we have enough // levels to do the first linear map. ctxt.modDownToLevel(thinRecrypt_initial_level); } #ifdef PRINT_LEVELS CheckCtxt(ctxt, "after mod down"); #endif // Move the slots to powerful-basis coefficients FHE_NTIMER_START(AAA_slotToCoeff); trcData.slotToCoeff->apply(ctxt); FHE_NTIMER_STOP(AAA_slotToCoeff); #ifdef PRINT_LEVELS CheckCtxt(ctxt, "after slotToCoeff"); #endif FHE_NTIMER_START(AAA_bootKeySwitch); // Make sure that this ciphertxt is in canonical form if (!ctxt.inCanonicalForm()) ctxt.reLinearize(); // Mod-switch down if needed IndexSet s = ctxt.getPrimeSet() / ctxt.getContext().specialPrimes; // set minus if (s.card()>2) { // leave only bottom two primes long frst = s.first(); long scnd = s.next(frst); IndexSet s2(frst,scnd); s.retain(s2); // retain only first two primes } ctxt.modDownToSet(s); // key-switch to the bootstrapping key ctxt.reLinearize(recryptKeyID); // "raw mod-switch" to the bootstrapping mosulus q=p^e+1. vector<ZZX> zzParts; // the mod-switched parts, in ZZX format double noise = ctxt.rawModSwitch(zzParts, q); noise = sqrt(noise); // Add multiples of p2r and q to make the zzParts divisible by p^{e'} long maxU=0; for (long i=0; i<(long)zzParts.size(); i++) { // make divisible by p^{e'} long newMax = makeDivisible(zzParts[i].rep, p2ePrime, p2r, q, trcData.alpha); zzParts[i].normalize(); // normalize after working directly on the rep if (maxU < newMax) maxU = newMax; } // Check that the estimated noise is still low if (noise + maxU*p2r*(skHwts[recryptKeyID]+1) > q/2) cerr << " * noise/q after makeDivisible = " << ((noise + maxU*p2r*(skHwts[recryptKeyID]+1))/q) << endl; for (long i=0; i<(long)zzParts.size(); i++) zzParts[i] /= p2ePrime; // divide by p^{e'} // Multiply the post-processed cipehrtext by the encrypted sKey #ifdef DEBUG_PRINTOUT cerr << "+ Before recryption "; decryptAndPrint(cerr, recryptEkey, *dbgKey, *dbgEa, printFlag); #endif double p0size = to_double(coeffsL2Norm(zzParts[0])); double p1size = to_double(coeffsL2Norm(zzParts[1])); ctxt = recryptEkey; ctxt.multByConstant(zzParts[1], p1size*p1size); ctxt.addConstant(zzParts[0], p0size*p0size); #ifdef DEBUG_PRINTOUT cerr << "+ Before linearTrans1 "; decryptAndPrint(cerr, ctxt, *dbgKey, *dbgEa, printFlag); #endif FHE_NTIMER_STOP(AAA_bootKeySwitch); #ifdef PRINT_LEVELS CheckCtxt(ctxt, "after bootKeySwitch"); #endif // Move the powerful-basis coefficients to the plaintext slots FHE_NTIMER_START(AAA_coeffToSlot); trcData.coeffToSlot->apply(ctxt); FHE_NTIMER_STOP(AAA_coeffToSlot); #ifdef PRINT_LEVELS CheckCtxt(ctxt, "after coeffToSlot"); #endif #ifdef DEBUG_PRINTOUT cerr << "+ After linearTrans1 "; decryptAndPrint(cerr, ctxt, *dbgKey, *dbgEa, printFlag); #endif // Extract the digits e-e'+r-1,...,e-e' (from fully packed slots) FHE_NTIMER_START(AAA_extractDigitsThin); extractDigitsThin(ctxt, e-ePrime, r, ePrime); FHE_NTIMER_STOP(AAA_extractDigitsThin); #ifdef PRINT_LEVELS CheckCtxt(ctxt, "after extractDigitsThin"); #endif #ifdef DEBUG_PRINTOUT cerr << "+ Before linearTrans2 "; decryptAndPrint(cerr, ctxt, *dbgKey, *dbgEa, printFlag); #endif }
void TestIt(long m, long p, long r, long d, long L, long bnd, long B) { cout << "*** TestIt" << (isDryRun()? "(dry run):" : ":") << " m=" << m << ", p=" << p << ", r=" << r << ", d=" << d << ", L=" << L << ", bnd=" << bnd << ", B=" << B << endl; setTimersOn(); FHEcontext context(m, p, r); buildModChain(context, L, /*c=*/2); context.zMStar.printout(); cout << endl; FHESecKey secretKey(context); const FHEPubKey& publicKey = secretKey; secretKey.GenSecKey(/*w=*/64); // A Hamming-weight-w secret key ZZX G; if (d == 0) G = context.alMod.getFactorsOverZZ()[0]; else G = makeIrredPoly(p, d); cout << "G = " << G << "\n"; cout << "generating key-switching matrices... "; addSome1DMatrices(secretKey); // compute key-switching matrices that we need cout << "done\n"; cout << "computing masks and tables for rotation..."; EncryptedArray ea(context, G); cout << "done\n"; PlaintextArray xp0(ea), xp1(ea); xp0.random(); xp1.random(); Ctxt xc0(publicKey); ea.encrypt(xc0, publicKey, xp0); ZZX poly_xp1; ea.encode(poly_xp1, xp1); cout << "** Testing replicate():\n"; bool error = false; Ctxt xc1 = xc0; CheckCtxt(xc1, "before replicate"); replicate(ea, xc1, ea.size()/2); if (!check_replicate(xc1, xc0, ea.size()/2, secretKey, ea)) error = true; CheckCtxt(xc1, "after replicate"); // Get some timing results for (long i=0; i<20 && i<ea.size(); i++) { xc1 = xc0; FHE_NTIMER_START(replicate); replicate(ea, xc1, i); if (!check_replicate(xc1, xc0, i, secretKey, ea)) error = true; FHE_NTIMER_STOP(replicate); } cout << " Replicate test " << (error? "failed :(\n" : "succeeded :)") << endl<< endl; printAllTimers(); cout << "\n** Testing replicateAll()... " << std::flush; #ifdef DEBUG_PRINTOUT replicateVerboseFlag = true; #else replicateVerboseFlag = false; #endif error = false; ReplicateTester *handler = new ReplicateTester(secretKey, ea, xp0, B); try { FHE_NTIMER_START(replicateAll); replicateAll(ea, xc0, handler, bnd); } catch (StopReplicate) { } cout << (handler->error? "failed :(\n" : "succeeded :)") << ", total time=" << handler->t_total << " (" << ((B>0)? B : ea.size()) << " vectors)\n"; delete handler; }
void TestIt(long p, long r, long c, long _k, long w, long L, Vec<long>& mvec, Vec<long>& gens, Vec<long>& ords, long useCache) { if (lsize(mvec)<1) { // use default values mvec.SetLength(3); gens.SetLength(3); ords.SetLength(3); mvec[0] = 7; mvec[1] = 3; mvec[2] = 221; gens[0] = 3979; gens[1] = 3095; gens[2] = 3760; ords[0] = 6; ords[1] = 2; ords[2] = -8; } if (!noPrint) cout << "*** TestIt" << (dry? " (dry run):" : ":") << " p=" << p << ", r=" << r << ", c=" << c << ", k=" << _k << ", w=" << w << ", L=" << L << ", mvec=" << mvec << ", " << ", useCache = " << useCache << endl; setTimersOn(); setDryRun(false); // Need to get a "real context" to test ThinEvalMap // mvec is supposed to include the prime-power factorization of m long nfactors = mvec.length(); for (long i = 0; i < nfactors; i++) for (long j = i+1; j < nfactors; j++) assert(GCD(mvec[i], mvec[j]) == 1); // multiply all the prime powers to get m itself long m = computeProd(mvec); assert(GCD(p, m) == 1); // build a context with these generators and orders vector<long> gens1, ords1; convert(gens1, gens); convert(ords1, ords); FHEcontext context(m, p, r, gens1, ords1); buildModChain(context, L, c); if (!noPrint) { context.zMStar.printout(); // print structure of Zm* /(p) to cout cout << endl; } long d = context.zMStar.getOrdP(); long phim = context.zMStar.getPhiM(); long nslots = phim/d; setDryRun(dry); // Now we can set the dry-run flag if desired FHESecKey secretKey(context); const FHEPubKey& publicKey = secretKey; secretKey.GenSecKey(w); // A Hamming-weight-w secret key addSome1DMatrices(secretKey); // compute key-switching matrices that we need addFrbMatrices(secretKey); // compute key-switching matrices that we need // GG defines the plaintext space Z_p[X]/GG(X) ZZX GG; GG = context.alMod.getFactorsOverZZ()[0]; EncryptedArray ea(context, GG); zz_p::init(context.alMod.getPPowR()); Vec<zz_p> val0(INIT_SIZE, nslots); for (auto& x: val0) random(x); vector<ZZX> val1; val1.resize(nslots); for (long i = 0; i < nslots; i++) { val1[i] = conv<ZZX>(conv<ZZ>(rep(val0[i]))); } Ctxt ctxt(publicKey); ea.encrypt(ctxt, publicKey, val1); resetAllTimers(); FHE_NTIMER_START(ALL); // Compute homomorphically the transformation that takes the // coefficients packed in the slots and produces the polynomial // corresponding to cube if (!noPrint) CheckCtxt(ctxt, "init"); if (!noPrint) cout << "build ThinEvalMap\n"; ThinEvalMap map(ea, /*minimal=*/false, mvec, /*invert=*/false, /*build_cache=*/false); // compute the transformation to apply if (!noPrint) cout << "apply ThinEvalMap\n"; if (useCache) map.upgrade(); map.apply(ctxt); // apply the transformation to ctxt if (!noPrint) CheckCtxt(ctxt, "ThinEvalMap"); if (!noPrint) cout << "check results\n"; if (!noPrint) cout << "build ThinEvalMap\n"; ThinEvalMap imap(ea, /*minimal=*/false, mvec, /*invert=*/true, /*build_cache=*/false); // compute the transformation to apply if (!noPrint) cout << "apply ThinEvalMap\n"; if (useCache) imap.upgrade(); imap.apply(ctxt); // apply the transformation to ctxt if (!noPrint) { CheckCtxt(ctxt, "ThinEvalMap"); cout << "check results\n"; } #if 1 /* create dirty version of ctxt */ Vec<zz_pX> dirty_val0; dirty_val0.SetLength(nslots); for (long i = 0; i < nslots; i++) { random(dirty_val0[i], d); SetCoeff(dirty_val0[i], 0, val0[i]); } vector<ZZX> dirty_val1; dirty_val1.resize(nslots); for (long i = 0; i < nslots; i++) { dirty_val1[i] = conv<ZZX>(dirty_val0[i]); } Ctxt dirty_ctxt(publicKey); ea.encrypt(dirty_ctxt, publicKey, dirty_val1); EvalMap dirty_map(ea, /*minimal=*/false, mvec, /*invert=*/false, /*build_cache=*/false); dirty_map.apply(dirty_ctxt); imap.apply(dirty_ctxt); #endif vector<ZZX> val2; ea.decrypt(ctxt, secretKey, val2); if (val1 == val2) cout << "ThinEvalMap: GOOD\n"; else cout << "ThinEvalMap: BAD\n"; #if 1 vector<ZZX> dirty_val2; ea.decrypt(dirty_ctxt, secretKey, dirty_val2); if (val1 == dirty_val2) cout << "ThinEvalMap: GOOD\n"; else cout << "ThinEvalMap: BAD\n"; #endif FHE_NTIMER_STOP(ALL); if (!noPrint) { cout << "\n*********\n"; printAllTimers(); cout << endl; } }
int main(int argc, char *argv[]) { ArgMapping amap; long m=53; amap.arg("m", m, "use specified value as modulus"); long p=17; amap.arg("p", p, "plaintext base"); long r=1; amap.arg("r", r, "lifting"); long levels=5; amap.arg("L", levels, "levels"); long nb_coeffs=5; amap.arg("n", nb_coeffs, "nb coefficients to extract"); amap.parse(argc, argv); cout << "\n\n******** generate parameters" << " m=" << m << ", p=" << p << ", r=" << r << ", n=" << nb_coeffs << endl; setTimersOn(); FHEcontext context(m, p, r); buildModChain(context, /*L=*/levels); // cout << context << endl; // context.zMStar.printout(); // cout << endl; cout << "Generating keys and key-switching matrices... " << std::flush; FHESecKey secretKey(context); secretKey.GenSecKey(/*w=*/64);// A Hamming-weight-w secret key addFrbMatrices(secretKey); // compute key-switching matrices that we need add1DMatrices(secretKey); // compute key-switching matrices that we need const FHEPubKey& publicKey = secretKey; cout << "done\n"; resetAllTimers(); EncryptedArray ea = *(context.ea); ea.buildLinPolyMat(false); Ctxt ctxt(publicKey); NewPlaintextArray ptxt(ea); random(ea, ptxt); // ea.encrypt(ctxt, publicKey, ptxt); ea.skEncrypt(ctxt, secretKey, ptxt); cout << "Extracting " << nb_coeffs << " coefficients..."; vector<Ctxt> coeffs; extractCoeffs(ea, coeffs, ctxt, nb_coeffs); cout << "done\n"; vector<ZZX> ptxtDec; ea.decrypt(ctxt, secretKey, ptxtDec); for (long i=0; i<(long)coeffs.size(); i++) { if (!coeffs[i].isCorrect()) { cerr << " potential decryption error for "<<i<<"th coeffs " << endl; CheckCtxt(coeffs[i], ""); exit(0); } vector<ZZX> pCoeffs; ea.decrypt(coeffs[i], secretKey, pCoeffs); assert(pCoeffs.size() == ptxtDec.size()); for (int j = 0; j < pCoeffs.size(); ++j) { if (coeff(pCoeffs[j], 0) != coeff(ptxtDec[j], i)) { cerr << "error: extracted coefficient " << i << " from " "slot " << j << " is " << coeff(pCoeffs[j], 0) << " instead of " << coeff(ptxtDec[j], i) << endl; exit(0); } } } cerr << "Extracted coefficient successfully verified!\n"; }