Exemplo n.º 1
0
int
main (void)
{
  struct xpr z, h, f, u;

  printf ("     Test of Exp Functions\n");
  z = xneg (xOne);
  h = atox ("0.5");
  u = atox ("3.01");
  for (; xprcmp (&z, &u) < 0; z = xadd (z, h, 0))
    {
      /* compute extended precision exponential */
      f = xexp (z);
      printf (" %8.4f  ", xtodbl (z));
      xprxpr (f, decd);
      f = xexp2 (z);
      printf ("\n           ");
      xprxpr (f, decd);
      f = xexp10 (z);
      printf ("\n           ");
      xprxpr (f, decd);
      putchar ('\n');
    }
  return 0;
}
Exemplo n.º 2
0
// key-switch to (1,s_i), s_i is the base key with index keyID. If
// keyID<0 then re-linearize to any key for which a switching matrix exists
void Ctxt::reLinearize(long keyID)
{
  // Special case: if *this is empty or already re-linearized then do nothing
  if (this->isEmpty() || this->inCanonicalForm(keyID)) return;

  FHE_TIMER_START;
  this->reduce();

  // To relinearize, the primeSet must be disjoint from the special primes
  if (!primeSet.disjointFrom(context.specialPrimes)) {
    modDownToSet(primeSet / context.specialPrimes);
    // cout << "<<< special primes\n";
  }

  long g = ptxtSpace;
  Ctxt tmp(pubKey, ptxtSpace); // an empty ciphertext, same plaintext space

  double logProd = context.logOfProduct(context.specialPrimes);
  tmp.noiseVar = noiseVar * xexp(2*logProd); // The noise after mod-UP

  for (size_t i=0; i<parts.size(); i++) {
    CtxtPart& part  = parts[i];

    // For a part relative to 1 or base,  only scale and add
    if (part.skHandle.isOne() || part.skHandle.isBase(keyID)) {
      part.addPrimesAndScale(context.specialPrimes);
      tmp.addPart(part, /*matchPrimeSet=*/true);
      continue;
    }
    // Look for a key-switching matrix to re-linearize this part
    const KeySwitch& W = (keyID>=0)? 
      pubKey.getKeySWmatrix(part.skHandle,keyID) :
      pubKey.getAnyKeySWmatrix(part.skHandle);

    assert(W.toKeyID>=0);       // verify that a switching matrix exists

    g = GCD(W.ptxtSpace, g);    // verify that the plaintext spaces match
    assert (g>1);
    tmp.ptxtSpace = g;

    
    tmp.keySwitchPart(part, W); // switch this part & update noiseVar
  }
  *this = tmp;

#if 0
  // alternative strategy: get rid of special primes
  // after each reLin...
  if (!primeSet.disjointFrom(context.specialPrimes)) {
    modDownToSet(primeSet / context.specialPrimes);
    // cout << ">>> special primes\n";
  }
#endif

  FHE_TIMER_STOP;
}
Exemplo n.º 3
0
double log_sum(const double *ptr, const size_t size) {
  if (size == 1)  /* do special case faster */
    return *ptr;
  else {
    const double max = *max_dbl(ptr, size);
    const double *end = ptr + size;
    double sum = 0;
    assert(size > 0);
    for (; ptr != end; ++ptr)
      sum += xexp(*ptr - max);
    return xlog(sum) + max;
  }
}
Exemplo n.º 4
0
static
void ComputeBKZThresh(xdouble *c, long beta)
{
   BKZThresh.SetLength(beta-1);

   long i;
   double x;

   x = 0;

   for (i = 1; i <= beta-1; i++) {
      x += log(c[i-1]);
      BKZThresh(i) = xexp(x/double(i))*BKZConstant(i);
   }
}
Exemplo n.º 5
0
// mod-switch down to primeSet \intersect s, after this call we have
// primeSet<=s. s must contain either all special primes or none of them.
void Ctxt::modDownToSet(const IndexSet &s)
{
  IndexSet intersection = primeSet & s;
  //  assert(!empty(intersection));       // some primes must be left
  if (empty(intersection)) {
    cerr << "modDownToSet called from "<<primeSet<<" to "<<s<<endl;
    exit(1);
  }
  if (intersection==primeSet) return; // nothing to do, removing no primes
  FHE_TIMER_START;

  IndexSet setDiff = primeSet / intersection; // set-minus

  // Scale down all the parts: use either a simple "drop down" (just removing
  // primes, i.e., reducing the ctxt modulo the samaller modulus), or a "real
  // modulus switching" with rounding, basically whichever yeilds smaller
  // noise. Recall that we keep the invariant that a ciphertext mod Q is
  // decrypted to Q*m (mod p), so if we just "drop down" we still need to
  // multiply by (Q^{-1} mod p).

  // Get an estimate for the added noise term for modulus switching
  xdouble addedNoiseVar = modSwitchAddedNoiseVar();
  if (noiseVar*ptxtSpace*ptxtSpace < addedNoiseVar) {     // just "drop down"
    long prodInv = InvMod(rem(context.productOfPrimes(setDiff),ptxtSpace), ptxtSpace);
    for (size_t i=0; i<parts.size(); i++) {
      parts[i].removePrimes(setDiff);         // remove the primes not in s
      parts[i] *= prodInv;
      // WARNING: the following line is written just so to prevent overflow
      noiseVar = noiseVar*prodInv*prodInv;
    }
    //    cerr << "DEGENERATE DROP\n";
  } 
  else {                                      // do real mod switching
    for (size_t i=0; i<parts.size(); i++) 
      parts[i].scaleDownToSet(intersection, ptxtSpace);

    // update the noise estimate
    double f = context.logOfProduct(setDiff);
    noiseVar /= xexp(2*f);
    noiseVar += addedNoiseVar;
  }
  primeSet.remove(setDiff); // remove the primes not in s
  assert(verifyPrimeSet()); // sanity-check: ensure primeSet is still valid
  FHE_TIMER_STOP;
}
Exemplo n.º 6
0
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;      
    }
  }
}
Exemplo n.º 7
0
// mod-switch up to add the primes in s \setminus primeSet, after this call we
// have s<=primeSet. s must contain either all special primes or none of them.
void Ctxt::modUpToSet(const IndexSet &s)
{
  //  FHE_TIMER_START;
  IndexSet setDiff = s/primeSet; // set minus (primes in s but not in primeSet)
  if (empty(setDiff)) return;    // nothing to do, no primes are added

  // scale up all the parts to use also the primes in setDiff
  double f = 0.0;
  for (long i=0; i<lsize(parts); i++) {
    // addPrimesAndScale returns the logarithm of the product of added primes,
    // all calls should return the same value = log(prod. of primes in setDiff)
    f = parts[i].addPrimesAndScale(setDiff);
  }

  // The variance estimate grows by a factor of exp(f)^2 = exp(2f)
  noiseVar *= xexp(2*f);

  primeSet.insert(setDiff); // add setDiff to primeSet
  assert(verifyPrimeSet()); // sanity-check: ensure primeSet is still valid
  //  FHE_TIMER_STOP;
}
Exemplo n.º 8
0
// Compute the number of digits that we need and the esitmated
// added noise from switching this ciphertext part.
static std::pair<long, NTL::xdouble>
keySwitchNoise(const CtxtPart& p, const FHEPubKey& pubKey, long pSpace)
{
  const FHEcontext& context = p.getContext();
  long nDigits = 0;
  xdouble addedNoise = to_xdouble(0.0);
  double sizeLeft = context.logOfProduct(p.getIndexSet());
  for (size_t i=0; i<context.digits.size() && sizeLeft>0.0; i++) {    
    nDigits++;

    double digitSize = context.logOfProduct(context.digits[i]);
    if (sizeLeft<digitSize) digitSize=sizeLeft;// need only part of this digit

    // Added noise due to this digit is phi(m) *sigma^2 *pSpace^2 *|Di|^2/4, 
    // where |Di| is the magnitude of the digit

    // WARNING: the following line is written just so to prevent overflow
    addedNoise += to_xdouble(context.zMStar.getPhiM()) * pSpace*pSpace
      * xexp(2*digitSize) * context.stdev*context.stdev / 4.0;

    sizeLeft -= digitSize;
  }

  // Sanity-check: make sure that the added noise is not more than the special
  // primes can handle: After dividing the added noise by the product of all
  // the special primes, it should be smaller than the added noise term due
  // to modulus switching, i.e., keyWeight * phi(m) * pSpace^2 / 12

  long keyWeight = pubKey.getSKeyWeight(p.skHandle.getSecretKeyID());
  double phim = context.zMStar.getPhiM();
  double logModSwitchNoise = log((double)keyWeight) 
    +2*log((double)pSpace) +log(phim) -log(12.0);
  double logKeySwitchNoise = log(addedNoise) 
    -2*context.logOfProduct(context.specialPrimes);
  assert(logKeySwitchNoise < logModSwitchNoise);

  return std::pair<long, NTL::xdouble>(nDigits,addedNoise);
}
Exemplo n.º 9
0
// Takes as arguments a ciphertext-part p relative to s' and a key-switching
// matrix W = W[s'->s], uses W to switch p relative to (1,s), and adds the
// result to *this.
// It is assumed that the part p does not include any of the special primes,
// and that if *this is not an empty ciphertext then its primeSet is
// p.getIndexSet() \union context.specialPrimes
void Ctxt::keySwitchPart(const CtxtPart& p, const KeySwitch& W)
{
  FHE_TIMER_START;

  // no special primes in the input part
  assert(context.specialPrimes.disjointFrom(p.getIndexSet()));

  // For parts p that point to 1 or s, only scale and add
  if (p.skHandle.isOne() || p.skHandle.isBase(W.toKeyID)) { 
    CtxtPart pp = p;
    pp.addPrimesAndScale(context.specialPrimes);
    addPart(pp, /*matchPrimeSet=*/true);
    return; 
  }

  // some sanity checks
  assert(W.fromKey == p.skHandle);  // the handles must match

  // Compute the number of digits that we need and the esitmated added noise
  // from switching this ciphertext part.
  long pSpace = W.ptxtSpace;
  long nDigits = 0;
  xdouble addedNoise = to_xdouble(0.0);
  double sizeLeft = context.logOfProduct(p.getIndexSet());
  for (size_t i=0; i<context.digits.size() && sizeLeft>0.0; i++) {    
    nDigits++;

    double digitSize = context.logOfProduct(context.digits[i]);
    if (sizeLeft<digitSize) digitSize=sizeLeft; // need only part of this digit

    // Added noise due to this digit is phi(m) * sigma^2 * pSpace^2 * |Di|^2/4, 
    // where |Di| is the magnitude of the digit

    // WARNING: the following line is written just so to prevent overflow
    addedNoise += to_xdouble(context.zMStar.getPhiM()) * pSpace*pSpace
      * xexp(2*digitSize) * context.stdev*context.stdev / 4.0;

    sizeLeft -= digitSize;
  }

  // Sanity-check: make sure that the added noise is not more than the special
  // primes can handle: After dividing the added noise by the product of all
  // the special primes, it should be smaller than the added noise term due
  // to modulus switching, i.e., keyWeight * phi(m) * pSpace^2 / 12

  long keyWeight = pubKey.getSKeyWeight(p.skHandle.getSecretKeyID());
  double phim = context.zMStar.getPhiM();
  double logModSwitchNoise = log((double)keyWeight) 
    +2*log((double)pSpace) +log(phim) -log(12.0);
  double logKeySwitchNoise = log(addedNoise) 
    -2*context.logOfProduct(context.specialPrimes);
  assert(logKeySwitchNoise < logModSwitchNoise);

  // Break the ciphertext part into digits, if needed, and scale up these
  // digits using the special primes. This is the most expensive operation
  // during homormophic evaluation, so it should be thoroughly optimized.

  vector<DoubleCRT> polyDigits;
  p.breakIntoDigits(polyDigits, nDigits);

  // Finally we multiply the vector of digits by the key-switching matrix

  // An object to hold the pseudorandom ai's, note that it must be defined
  // with the maximum number of levels, else the PRG will go out of synch.
  // FIXME: This is a bug waiting to happen.
  DoubleCRT ai(context);

  // Set the first ai using the seed, subsequent ai's (if any) will
  // use the evolving RNG state (NOTE: this is not thread-safe)

  RandomState state;
  SetSeed(W.prgSeed);

  // Add the columns in, one by one
  DoubleCRT tmp(context, IndexSet::emptySet());
  
  for (unsigned long i=0; i<polyDigits.size(); i++) {
    ai.randomize();
    tmp = polyDigits[i];
  
    // The operations below all use the IndexSet of tmp
  
    // add part*a[i] with a handle pointing to base of W.toKeyID
    tmp.Mul(ai,  /*matchIndexSet=*/false);
    addPart(tmp, SKHandle(1,1,W.toKeyID), /*matchPrimeSet=*/true);
  
    // add part*b[i] with a handle pointing to one
    polyDigits[i].Mul(W.b[i], /*matchIndexSet=*/false);
    addPart(polyDigits[i], SKHandle(), /*matchPrimeSet=*/true);
  }
  noiseVar += addedNoise;
} // restore random state upon destruction of the RandomState, see NumbTh.h
Exemplo n.º 10
0
// Encrypts plaintext, result returned in the ciphertext argument. The
// returned value is the plaintext-space for that ciphertext. When called
// with highNoise=true, returns a ciphertext with noise level~q/8.
long FHEPubKey::Encrypt(Ctxt &ctxt, const ZZX& ptxt, long ptxtSpace,
			bool highNoise) const
{
  FHE_TIMER_START;
  assert(this == &ctxt.pubKey);

  if (ptxtSpace != pubEncrKey.ptxtSpace) { // plaintext-space mistamtch
    ptxtSpace = GCD(ptxtSpace, pubEncrKey.ptxtSpace);
    if (ptxtSpace <= 1) Error("Plaintext-space mismatch on encryption");
  }

  // generate a random encryption of zero from the public encryption key
  ctxt = pubEncrKey;  // already an encryption of zero, just not a random one

  // choose a random small scalar r and a small random error vector e,
  // then set ctxt = r*pubEncrKey + ptstSpace*e + (ptxt,0)
  DoubleCRT e(context, context.ctxtPrimes);
  DoubleCRT r(context, context.ctxtPrimes);
  r.sampleSmall();

  for (size_t i=0; i<ctxt.parts.size(); i++) {  // add noise to all the parts
    ctxt.parts[i] *= r;

    if (highNoise && i == 0) {
      // we sample e so that coefficients are uniform over 
      // [-Q/(8*ptxtSpace)..Q/(8*ptxtSpace)]

      ZZ B;
      B = context.productOfPrimes(context.ctxtPrimes);
      B /= ptxtSpace;
      B /= 8;
      e.sampleUniform(B);
    }
    else { 
      e.sampleGaussian();
    }

    e *= ptxtSpace;
    ctxt.parts[i] += e;
  }

  // add in the plaintext
  // FIXME: This relies on the first part, ctxt[0], to have handle to 1
  if (ptxtSpace==2) ctxt.parts[0] += ptxt;

  else { // The general case of ptxtSpace>2: for a ciphertext
         // relative to modulus Q, we add ptxt * Q mod ptxtSpace.
    long QmodP = rem(context.productOfPrimes(ctxt.primeSet), ptxtSpace);
    ctxt.parts[0] += MulMod(ptxt,QmodP,ptxtSpace); // MulMod from module NumbTh
  }

  // fill in the other ciphertext data members
  ctxt.ptxtSpace = ptxtSpace;

  if (highNoise) {
    // hack: we set noiseVar to Q^2/8, which is just below threshold 
    // that will signal an error

    ctxt.noiseVar = xexp(2*context.logOfProduct(context.ctxtPrimes) - log(8.0));

  }
  else {
    // We have <skey,ctxt>= r*<skey,pkey> +p*(e0+e1*s) +m, where VAR(<skey,pkey>)
    // is recorded in pubEncrKey.noiseVar, VAR(ei)=sigma^2*phi(m), and VAR(s) is
    // determined by the secret-key Hamming weight (skHwt). 
    // VAR(r)=phi(m)/2, hence the expected size squared is bounded by:
    // E(X^2) <= pubEncrKey.noiseVar *phi(m) *stdev^2
    //                               + p^2*sigma^2 *phi(m) *(skHwt+1) + p^2
  
    long hwt = skHwts[0];
    xdouble phim = to_xdouble(context.zMStar.getPhiM());
    xdouble sigma2 = context.stdev * context.stdev;
    xdouble p2 = to_xdouble(ptxtSpace) * to_xdouble(ptxtSpace);
    ctxt.noiseVar = pubEncrKey.noiseVar*phim*0.5 
                    + p2*sigma2*phim*(hwt+1)*context.zMStar.get_cM() + p2;
  }
  return ptxtSpace;
}
Exemplo n.º 11
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);
  }
}