NTL_CLIENT // Compute a degree-p polynomial poly(x) s.t. for any t<e and integr z of the // form z = z0 + p^t*z1 (with 0<=z0<p), we have poly(z) = z0 (mod p^{t+1}). // // We get poly(x) by interpolating a degree-(p-1) polynomial poly'(x) // s.t. poly'(z0)=z0 - z0^p (mod p^e) for all 0<=z0<p, and then setting // poly(x) = x^p + poly'(x). static void buildDigitPolynomial(ZZX& result, long p, long e) { if (p<2 || e<=1) return; // nothing to do FHE_TIMER_START; long p2e = power_long(p,e); // the integer p^e // Compute x - x^p (mod p^e), for x=0,1,...,p-1 vec_long x(INIT_SIZE, p); vec_long y(INIT_SIZE, p); long bottom = -(p/2); for (long j=0; j<p; j++) { long z = bottom+j; x[j] = z; y[j] = z-PowerMod((z < 0 ? z + p2e : z), p, p2e); // x - x^p (mod p^e) while (y[j] > p2e/2) y[j] -= p2e; while (y[j] < -(p2e/2)) y[j] += p2e; } interpolateMod(result, x, y, p, e); assert(deg(result)<p); // interpolating p points, should get deg<=p-1 SetCoeff(result, p); // return result = x^p + poly'(x) // cerr << "# digitExt mod "<<p<<"^"<<e<<"="<<result<<endl; FHE_TIMER_STOP; }
void PAlgebraLift(const ZZX& phimx, const vec_zz_pX& lfactors, vec_zz_pX& factors, vec_zz_pX& crtc, long r) { long p = zz_p::modulus(); long nSlots = lfactors.length(); vec_ZZX vzz; // need to go via ZZX // lift the factors of Phi_m(X) from mod-2 to mod-2^r if (lfactors.length() > 1) MultiLift(vzz, lfactors, phimx, r); // defined in NTL::ZZXFactoring else { vzz.SetLength(1); vzz[0] = phimx; } // Compute the zz_pContext object for mod p^r arithmetic zz_p::init(power_long(p, r)); zz_pX phimxmod = to_zz_pX(phimx); factors.SetLength(nSlots); for (long i=0; i<nSlots; i++) // Convert from ZZX to zz_pX conv(factors[i], vzz[i]); // Finally compute the CRT coefficients for the factors crtc.SetLength(nSlots); for (long i=0; i<nSlots; i++) { zz_pX& fct = factors[i]; zz_pX te = phimxmod / fct; // \prod_{j\ne i} Fj te %= fct; // \prod_{j\ne i} Fj mod Fi InvModpr(crtc[i], te, fct, p, r);// \prod_{j\ne i} Fj^{-1} mod Fi } }
// Prime-power factorization void pp_factorize(vector<long>& factors, long N) { Vec< Pair<long, long> > pf; factorize(pf,N); // prime factors, N = \prod_i pf[i].first^{pf[i].second} factors.resize(pf.length()); for (long i=0; i<pf.length(); i++) factors[i] = power_long(pf[i].a, pf[i].b); // p_i^e_i }
/* Compute Phi(N) */ long phi_N(long N) { long phiN=1,p,e; PrimeSeq s; while (N!=1) { p=s.next(); e=0; while ((N%p)==0) { N=N/p; e++; } if (e!=0) { phiN=phiN*(p-1)*power_long(p,e-1); } } return phiN; }
// Interpolate the integer polynomial such that poly(x[i] mod p)=y[i] (mod p^e) // It is assumed that the points x[i] are all distinct modulo p void interpolateMod(ZZX& poly, const vec_long& x, const vec_long& y, long p, long e) { poly = ZZX::zero(); // initialize to zero long p2e = power_long(p,e); // p^e vec_long ytmp(INIT_SIZE, y.length()); // A temporary writable copy for (long j=0; j<y.length(); j++) { ytmp[j] = y[j] % p2e; if (ytmp[j] < 0) ytmp[j] += p2e; } zz_pBak bak; bak.save(); // Set the current modulus to p zz_p::init(p); vec_zz_p xmod(INIT_SIZE, x.length()); // convert to zz_p for (long j=0; j<x.length(); j++) xmod[j] = to_zz_p(x[j] % p); vec_zz_p ymod(INIT_SIZE, y.length()); // scratch space recursiveInterpolateMod(poly, x, ytmp, xmod, ymod, p, p2e); }
// prime power solver // zz_p::modulus() is assumed to be p^r, for p prime, r >= 1 // A is an n x n matrix, b is a length n (row) vector, // and a solution for the matrix-vector equation x A = b is found. // If A is not inverible mod p, then error is raised. void ppsolve(vec_zz_pE& x, const mat_zz_pE& A, const vec_zz_pE& b, long p, long r) { if (r == 1) { zz_pE det; solve(det, x, A, b); if (det == 0) Error("ppsolve: matrix not invertible"); return; } long n = A.NumRows(); if (n != A.NumCols()) Error("ppsolve: matrix not square"); if (n == 0) Error("ppsolve: matrix of dimension 0"); zz_pContext pr_context; pr_context.save(); zz_pEContext prE_context; prE_context.save(); zz_pX G = zz_pE::modulus(); ZZX GG = to_ZZX(G); vector< vector<ZZX> > AA; convert(AA, A); vector<ZZX> bb; convert(bb, b); zz_pContext p_context(p); p_context.restore(); zz_pX G1 = to_zz_pX(GG); zz_pEContext pE_context(G1); pE_context.restore(); // we are now working mod p... // invert A mod p mat_zz_pE A1; convert(A1, AA); mat_zz_pE I1; zz_pE det; inv(det, I1, A1); if (det == 0) { Error("ppsolve: matrix not invertible"); } vec_zz_pE b1; convert(b1, bb); vec_zz_pE y1; y1 = b1 * I1; vector<ZZX> yy; convert(yy, y1); // yy is a solution mod p for (long k = 1; k < r; k++) { // lift solution yy mod p^k to a solution mod p^{k+1} pr_context.restore(); prE_context.restore(); // we are now working mod p^r vec_zz_pE d, y; convert(y, yy); d = b - y * A; vector<ZZX> dd; convert(dd, d); long pk = power_long(p, k); vector<ZZX> ee; div(ee, dd, pk); p_context.restore(); pE_context.restore(); // we are now working mod p vec_zz_pE e1; convert(e1, ee); vec_zz_pE z1; z1 = e1 * I1; vector<ZZX> zz, ww; convert(zz, z1); mul(ww, zz, pk); add(yy, yy, ww); } pr_context.restore(); prE_context.restore(); convert(x, yy); assert(x*A == b); }
// Testing the I/O of the important classes of the library // (context, keys, ciphertexts). int main(int argc, char *argv[]) { ArgMapping amap; long r=1; long p=2; long c = 2; long w = 64; long L = 5; long mm=0; amap.arg("p", p, "plaintext base"); amap.arg("r", r, "lifting"); amap.arg("c", c, "number of columns in the key-switching matrices"); amap.arg("m", mm, "cyclotomic index","{31,127,1023}"); amap.parse(argc, argv); bool useTable = (mm==0 && p==2); long ptxtSpace = power_long(p,r); long numTests = useTable? N_TESTS : 1; std::unique_ptr<FHEcontext> contexts[numTests]; std::unique_ptr<FHESecKey> sKeys[numTests]; std::unique_ptr<Ctxt> ctxts[numTests]; std::unique_ptr<EncryptedArray> eas[numTests]; vector<ZZX> ptxts[numTests]; // first loop: generate stuff and write it to cout // open file for writing {fstream keyFile("iotest.txt", fstream::out|fstream::trunc); assert(keyFile.is_open()); for (long i=0; i<numTests; i++) { long m = (mm==0)? ms[i][1] : mm; cout << "Testing IO: m="<<m<<", p^r="<<p<<"^"<<r<<endl; Vec<long> mvec(INIT_SIZE,2); mvec[0] = ms[i][4]; mvec[1] = ms[i][5]; vector<long> gens(2); gens[0] = ms[i][6]; gens[1] = ms[i][7]; vector<long> ords(2); ords[0] = ms[i][8]; ords[1] = ms[i][9]; if (useTable && gens[0]>0) contexts[i].reset(new FHEcontext(m, p, r, gens, ords)); else contexts[i].reset(new FHEcontext(m, p, r)); contexts[i]->zMStar.printout(); buildModChain(*contexts[i], L, c); // Set the modulus chain if (mm==0 && m==1023) contexts[i]->makeBootstrappable(mvec); // Output the FHEcontext to file writeContextBase(keyFile, *contexts[i]); writeContextBase(cout, *contexts[i]); keyFile << *contexts[i] << endl; sKeys[i].reset(new FHESecKey(*contexts[i])); const FHEPubKey& publicKey = *sKeys[i]; sKeys[i]->GenSecKey(w,ptxtSpace); // A Hamming-weight-w secret key addSome1DMatrices(*sKeys[i]);// compute key-switching matrices that we need eas[i].reset(new EncryptedArray(*contexts[i])); long nslots = eas[i]->size(); // Output the secret key to file, twice. Below we will have two copies // of most things. keyFile << *sKeys[i] << endl;; keyFile << *sKeys[i] << endl;; vector<ZZX> b; long p2r = eas[i]->getContext().alMod.getPPowR(); ZZX poly = RandPoly(0,to_ZZ(p2r)); // choose a random constant polynomial eas[i]->decode(ptxts[i], poly); ctxts[i].reset(new Ctxt(publicKey)); eas[i]->encrypt(*ctxts[i], publicKey, ptxts[i]); eas[i]->decrypt(*ctxts[i], *sKeys[i], b); assert(ptxts[i].size() == b.size()); for (long j = 0; j < nslots; j++) assert (ptxts[i][j] == b[j]); // output the plaintext keyFile << "[ "; for (long j = 0; j < nslots; j++) keyFile << ptxts[i][j] << " "; keyFile << "]\n"; eas[i]->encode(poly,ptxts[i]); keyFile << poly << endl; // Output the ciphertext to file keyFile << *ctxts[i] << endl; keyFile << *ctxts[i] << endl; cerr << "okay " << i << endl<< endl; } keyFile.close();} cerr << "so far, so good\n\n"; // second loop: read from input and repeat the computation // open file for read {fstream keyFile("iotest.txt", fstream::in); for (long i=0; i<numTests; i++) { // Read context from file unsigned long m1, p1, r1; vector<long> gens, ords; readContextBase(keyFile, m1, p1, r1, gens, ords); FHEcontext tmpContext(m1, p1, r1, gens, ords); keyFile >> tmpContext; assert (*contexts[i] == tmpContext); cerr << i << ": context matches input\n"; // We define some things below wrt *contexts[i], not tmpContext. // This is because the various operator== methods check equality of // references, not equality of the referenced FHEcontext objects. FHEcontext& context = *contexts[i]; FHESecKey secretKey(context); FHESecKey secretKey2(tmpContext); const FHEPubKey& publicKey = secretKey; const FHEPubKey& publicKey2 = secretKey2; keyFile >> secretKey; keyFile >> secretKey2; assert(secretKey == *sKeys[i]); cerr << " secret key matches input\n"; EncryptedArray ea(context); EncryptedArray ea2(tmpContext); long nslots = ea.size(); // Read the plaintext from file vector<ZZX> a; a.resize(nslots); assert(nslots == (long)ptxts[i].size()); seekPastChar(keyFile, '['); // defined in NumbTh.cpp for (long j = 0; j < nslots; j++) { keyFile >> a[j]; assert(a[j] == ptxts[i][j]); } seekPastChar(keyFile, ']'); cerr << " ptxt matches input\n"; // Read the encoded plaintext from file ZZX poly1, poly2; keyFile >> poly1; eas[i]->encode(poly2,a); assert(poly1 == poly2); cerr << " eas[i].encode(a)==poly1 okay\n"; ea.encode(poly2,a); assert(poly1 == poly2); cerr << " ea.encode(a)==poly1 okay\n"; ea2.encode(poly2,a); assert(poly1 == poly2); cerr << " ea2.encode(a)==poly1 okay\n"; eas[i]->decode(a,poly1); assert(nslots == (long)a.size()); for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]); cerr << " eas[i].decode(poly1)==ptxts[i] okay\n"; ea.decode(a,poly1); assert(nslots == (long)a.size()); for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]); cerr << " ea.decode(poly1)==ptxts[i] okay\n"; ea2.decode(a,poly1); assert(nslots == (long)a.size()); for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]); cerr << " ea2.decode(poly1)==ptxts[i] okay\n"; // Read ciperhtext from file Ctxt ctxt(publicKey); Ctxt ctxt2(publicKey2); keyFile >> ctxt; keyFile >> ctxt2; assert(ctxts[i]->equalsTo(ctxt,/*comparePkeys=*/false)); cerr << " ctxt matches input\n"; sKeys[i]->Decrypt(poly2,*ctxts[i]); assert(poly1 == poly2); cerr << " sKeys[i]->decrypt(*ctxts[i]) == poly1 okay\n"; secretKey.Decrypt(poly2,*ctxts[i]); assert(poly1 == poly2); cerr << " secretKey.decrypt(*ctxts[i]) == poly1 okay\n"; secretKey.Decrypt(poly2,ctxt); assert(poly1 == poly2); cerr << " secretKey.decrypt(ctxt) == poly1 okay\n"; secretKey2.Decrypt(poly2,ctxt2); assert(poly1 == poly2); cerr << " secretKey2.decrypt(ctxt2) == poly1 okay\n"; eas[i]->decrypt(ctxt, *sKeys[i], a); assert(nslots == (long)a.size()); for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]); cerr << " eas[i].decrypt(ctxt, *sKeys[i])==ptxts[i] okay\n"; ea.decrypt(ctxt, secretKey, a); assert(nslots == (long)a.size()); for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]); cerr << " ea.decrypt(ctxt, secretKey)==ptxts[i] okay\n"; ea2.decrypt(ctxt2, secretKey2, a); assert(nslots == (long)a.size()); for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]); cerr << " ea2.decrypt(ctxt2, secretKey2)==ptxts[i] okay\n"; cerr << "test "<<i<<" okay\n\n"; }} unlink("iotest.txt"); // clean up before exiting }
// Extract digits from fully packed slots void extractDigitsPacked(Ctxt& ctxt, long botHigh, long r, long ePrime, const vector<ZZX>& unpackSlotEncoding) { FHE_TIMER_START; // Step 1: unpack the slots of ctxt FHE_NTIMER_START(unpack); ctxt.cleanUp(); // Apply the d automorphisms and store them in scratch area long d = ctxt.getContext().zMStar.getOrdP(); vector<Ctxt> scratch; // used below vector<Ctxt> unpacked(d, Ctxt(ZeroCtxtLike, ctxt)); { // explicit scope to force all temporaries to be released vector< shared_ptr<DoubleCRT> > coeff_vector; coeff_vector.resize(d); for (long i = 0; i < d; i++) coeff_vector[i] = shared_ptr<DoubleCRT>(new DoubleCRT(unpackSlotEncoding[i], ctxt.getContext(), ctxt.getPrimeSet()) ); Ctxt tmp1(ZeroCtxtLike, ctxt); Ctxt tmp2(ZeroCtxtLike, ctxt); // FIXME: implement using hoisting! for (long j = 0; j < d; j++) { // process jth Frobenius tmp1 = ctxt; tmp1.frobeniusAutomorph(j); tmp1.cleanUp(); // FIXME: not clear if we should call cleanUp here for (long i = 0; i < d; i++) { tmp2 = tmp1; tmp2.multByConstant(*coeff_vector[mcMod(i+j, d)]); unpacked[i] += tmp2; } } } FHE_NTIMER_STOP(unpack); // Step 2: extract the digits top-1,...,0 from the slots of unpacked[i] long p = ctxt.getContext().zMStar.getP(); long p2r = power_long(p,r); long topHigh = botHigh + r-1; #ifdef DEBUG_PRINTOUT cerr << "+ After unpack "; decryptAndPrint(cerr, unpacked[0], *dbgKey, *dbgEa, printFlag); cerr << " extracting "<<(topHigh+1)<<" digits\n"; #endif if (p==2 && r>2) topHigh--; // For p==2 we sometime get a bit for free FHE_NTIMER_START(extractDigits); for (long i=0; i<(long)unpacked.size(); i++) { if (topHigh<=0) { // extracting LSB = no-op scratch.assign(1,unpacked[i]); } else { // extract digits topHigh...0, store them in scratch extractDigits(scratch, unpacked[i], topHigh+1); } // set upacked[i] = -\sum_{j=botHigh}^{topHigh} scratch[j] * p^{j-botHigh} if (topHigh >= (long)scratch.size()) { topHigh = scratch.size() -1; cerr << " @ suspect: not enough digits in extractDigitsPacked\n"; } unpacked[i] = scratch[topHigh]; for (long j=topHigh-1; j>=botHigh; --j) { unpacked[i].multByP(); unpacked[i] += scratch[j]; } if (p==2 && botHigh>0) // For p==2, subtract also the previous bit unpacked[i] += scratch[botHigh-1]; unpacked[i].negate(); if (r>ePrime) { // Add in digits from the bottom part, if any long topLow = r-1 - ePrime; Ctxt tmp = scratch[topLow]; for (long j=topLow-1; j>=0; --j) { tmp.multByP(); tmp += scratch[j]; } if (ePrime>0) tmp.multByP(ePrime); // multiply by p^e' unpacked[i] += tmp; } unpacked[i].reducePtxtSpace(p2r); // Our plaintext space is now mod p^r } FHE_NTIMER_STOP(extractDigits); #ifdef DEBUG_PRINTOUT cerr << "+ Before repack "; decryptAndPrint(cerr, unpacked[0], *dbgKey, *dbgEa, printFlag); #endif // Step 3: re-pack the slots FHE_NTIMER_START(repack); const EncryptedArray& ea2 = *ctxt.getContext().ea; ZZX xInSlots; vector<ZZX> xVec(ea2.size()); ctxt = unpacked[0]; for (long i=1; i<d; i++) { x2iInSlots(xInSlots, i, xVec, ea2); unpacked[i].multByConstant(xInSlots); ctxt += unpacked[i]; } FHE_NTIMER_STOP(repack); }
// bootstrap a ciphertext to reduce noise void FHEPubKey::reCrypt(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 = getContext().zMStar.getP(); long r = getContext().alMod.getR(); long p2r = getContext().alMod.getPPowR(); // the bootstrapping key is encrypted relative to plaintext space p^{e-e'+r}. long e = getContext().rcData.e; long ePrime = getContext().rcData.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); FHE_NTIMER_START(preProcess); // Make sure that this ciphertxt is in canonical form if (!ctxt.inCanonicalForm()) ctxt.reLinearize(); // Mod-switch down if needed IndexSet s = ctxt.getPrimeSet() / 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, getContext().rcData.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(preProcess); // Move the powerful-basis coefficients to the plaintext slots FHE_NTIMER_START(LinearTransform1); ctxt.getContext().rcData.firstMap->apply(ctxt); FHE_NTIMER_STOP(LinearTransform1); #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) extractDigitsPacked(ctxt, e-ePrime, r, ePrime, context.rcData.unpackSlotEncoding); #ifdef DEBUG_PRINTOUT cerr << "+ Before linearTrans2 "; decryptAndPrint(cerr, ctxt, *dbgKey, *dbgEa, printFlag); #endif // Move the slots back to powerful-basis coefficients FHE_NTIMER_START(LinearTransform2); ctxt.getContext().rcData.secondMap->apply(ctxt); FHE_NTIMER_STOP(LinearTransform2); }
// Extract digits from thinly packed slots void extractDigitsThin(Ctxt& ctxt, long botHigh, long r, long ePrime) { FHE_TIMER_START; Ctxt unpacked(ctxt); unpacked.cleanUp(); vector<Ctxt> scratch; // Step 2: extract the digits top-1,...,0 from the slots of unpacked[i] long p = ctxt.getContext().zMStar.getP(); long p2r = power_long(p,r); long topHigh = botHigh + r-1; #ifdef DEBUG_PRINTOUT cerr << "+ After unpack "; decryptAndPrint(cerr, unpacked, *dbgKey, *dbgEa, printFlag); cerr << " extracting "<<(topHigh+1)<<" digits\n"; #endif if (p==2 && r>2) topHigh--; // For p==2 we sometime get a bit for free if (topHigh<=0) { // extracting LSB = no-op scratch.assign(1, unpacked); } else { // extract digits topHigh...0, store them in scratch extractDigits(scratch, unpacked, topHigh+1); } // set upacked = -\sum_{j=botHigh}^{topHigh} scratch[j] * p^{j-botHigh} if (topHigh >= LONG(scratch.size())) { topHigh = scratch.size() -1; cerr << " @ suspect: not enough digits in extractDigitsPacked\n"; } unpacked = scratch[topHigh]; for (long j=topHigh-1; j>=botHigh; --j) { unpacked.multByP(); unpacked += scratch[j]; } if (p==2 && botHigh>0) // For p==2, subtract also the previous bit unpacked += scratch[botHigh-1]; unpacked.negate(); if (r>ePrime) { // Add in digits from the bottom part, if any long topLow = r-1 - ePrime; Ctxt tmp = scratch[topLow]; for (long j=topLow-1; j>=0; --j) { tmp.multByP(); tmp += scratch[j]; } if (ePrime>0) tmp.multByP(ePrime); // multiply by p^e' unpacked += tmp; } unpacked.reducePtxtSpace(p2r); // Our plaintext space is now mod p^r #ifdef DEBUG_PRINTOUT cerr << "+ Before repack "; decryptAndPrint(cerr, unpacked[0], *dbgKey, *dbgEa, printFlag); #endif ctxt = unpacked; }
int main(int argc, char *argv[]) { argmap_t argmap; argmap["p"] = "5"; argmap["m"] = "101"; argmap["r"] = "1"; if (!parseArgs(argc, argv, argmap)) usage(argv[0]); long p = atoi(argmap["p"]); long m = atoi(argmap["m"]); long r = atoi(argmap["r"]); cout << "p=" << p << ", m=" << m << ", r=" << r << "\n"; ZZX phimx = Cyclotomic(m); zz_p::init(p); zz_pX phimx_modp = to_zz_pX(phimx); vec_zz_pX factors = SFCanZass(phimx_modp); vec_ZZX FFactors; MultiLift(FFactors, factors, phimx, r); zz_p::init(power_long(p, r)); vec_zz_pX Factors; Factors.SetLength(FFactors.length()); for (long i = 0; i < Factors.length(); i++) conv(Factors[i], FFactors[i]); zz_pX G = Factors[0]; long d = deg(G); cout << "d=" << d << "\n"; zz_pE::init(G); // L selects the even coefficients vec_zz_pE L; L.SetLength(d); for (long j = 0; j < d; j++) { if (j % 2 == 0) L[j] = to_zz_pE(zz_pX(j, 1)); } vec_zz_pE C; buildLinPolyCoeffs(C, L, p, r); zz_pE alpha, beta; random(alpha); applyLinPoly(beta, C, alpha, p); cout << alpha << "\n"; cout << beta << "\n"; }