Exemplo n.º 1
0
void adjustLevelForMult(Ctxt& c1, const char name1[], const ZZX& p1,
			Ctxt& c2, const char name2[], const ZZX& p2, 
			const FHESecKey& sk)
{
  const FHEcontext& context = c1.getContext();

  // The highest possible level for this multiplication is the
  // intersection of the two primeSets, without the special primes.
  IndexSet primes = c1.getPrimeSet() & c2.getPrimeSet();
  primes.remove(context.specialPrimes);
  assert (!empty(primes));

  //  double phim = (double) context.zMstar.phiM();
  //  double factor = c_m*sqrt(log(phim))*4;

  xdouble n1,n2,d1,d2;
  xdouble dvar1 = c1.modSwitchAddedNoiseVar();
  xdouble dvar2 = c2.modSwitchAddedNoiseVar();
  //  xdouble dmag1 = c1.modSwitchAddedNoiseMag(c_m);
  //  xdouble dmag2 = c2.modSwitchAddedNoiseMag(c_m);
  //  cout << " ** log(dvar1)=" << log(dvar1) 
  //       << ", log(dvar2)=" << log(dvar2) <<endl;

  double logF1, logF2;
  xdouble n1var, n2var, modSize; // n1mag, n2mag,
  // init to large number
  xdouble noiseVarRatio=xexp(2*(context.logOfProduct(context.ctxtPrimes)
				+ context.logOfProduct(context.specialPrimes)));
  //  xdouble noiseMagRatio=noiseVarRatio;

  // Find the level that minimizes the noise-to-modulus ratio
  bool oneLevelMore = false;
  for (IndexSet levelDown = primes; 
       !empty(levelDown); levelDown.remove(levelDown.last())) {

    // compute noise variane/magnitude after mod-switchign to this level
    logF1 = context.logOfProduct(c1.getPrimeSet() / levelDown);
    n1var = c1.getNoiseVar()/xexp(2*logF1);

    logF2 = context.logOfProduct(c2.getPrimeSet() / levelDown);
    n2var = c2.getNoiseVar()/xexp(2*logF2);

    // compute modulus/noise ratio at this level
    modSize = xexp(context.logOfProduct(levelDown));
    xdouble nextNoiseVarRatio = sqrt((n1var+dvar1)*(n2var+dvar2))/modSize;

    if (nextNoiseVarRatio < 2*noiseVarRatio || oneLevelMore) {
      noiseVarRatio = nextNoiseVarRatio;
      primes = levelDown;  // record the currently best prime set
      n1 = n1var; d1=dvar1; n2 = n2var; d2=dvar2;
    }
    oneLevelMore = (n1var > dvar1 || n2var > dvar2);
  }

  if (primes < c1.getPrimeSet()) {
    cout << "          ** " << c1.getPrimeSet()<<"=>"<<primes << endl;
    cout << "             n1var="<<n1<<", d1var="<<d1<<endl;;
    c1.modDownToSet(primes);
    cout << name1 << ".mDown:"; checkCiphertext(c1, p1, sk);
  }
  if (primes < c2.getPrimeSet()) {
    cout << "          ** " << c2.getPrimeSet()<<"=>"<<primes << endl;
    cout << "             n2var="<<n2<<", d2var="<<d2<<endl;;
    c2.modDownToSet(primes);
    cout << name2 << ".mDown:"; checkCiphertext(c2, p2, sk);
  }
}
Exemplo n.º 2
0
// 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);
}