Exemple #1
0
// Add a constant polynomial
void Ctxt::addConstant(const DoubleCRT& dcrt, double size)
{
  FHE_TIMER_START;
  // If the size is not given, we use the default value phi(m)*(ptxtSpace/2)^2
  if (size < 0.0) {
    // WARNING: the following line is written just so to prevent overflow
    size = ((double) context.zMStar.getPhiM()) * ptxtSpace*ptxtSpace /4.0;
  }

  // Scale the constant, then add it to the part that points to one
  long f = (ptxtSpace>2)? rem(context.productOfPrimes(primeSet),ptxtSpace): 1;
  if (f!=1) {
    DoubleCRT tmp = dcrt;
    tmp *= f;
    addPart(tmp, SKHandle(0,1,0));
  }
  else addPart(dcrt, SKHandle(0,1,0));

  noiseVar += (size*f)*f;
  FHE_TIMER_STOP;
}
Exemple #2
0
// Multiply vector of digits by key-switching matrix and add to *this.
// It is assumed that W has at least as many b[i]'s as there are digits.
// The vector of digits is modified in place.
void Ctxt::keySwitchDigits(const KeySwitch& W, vector<DoubleCRT>& digits)
{  // 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);

  // Subsequent ai's use the evolving RNG state
  RandomState state; // backup the NTL PRG seed
  NTL::SetSeed(W.prgSeed);

  // Add the columns in, one by one
  DoubleCRT tmpDCRT(context, IndexSet::emptySet());  
  for (size_t i=0; i<digits.size(); i++) {
    FHE_NTIMER_START(KS_loop);
    ai.randomize();
    tmpDCRT = digits[i];
  
    // The operations below all use the IndexSet of tmpDCRT
  
    // add digit*a[i] with a handle pointing to base of W.toKeyID
{ FHE_NTIMER_START(KS_loop_1);
    tmpDCRT.Mul(ai,  /*matchIndexSet=*/false);
}

{ FHE_NTIMER_START(KS_loop_2);
    this->addPart(tmpDCRT, SKHandle(1,1,W.toKeyID), /*matchPrimeSet=*/true);
}
  
    // add digit*b[i] with a handle pointing to one
{ FHE_NTIMER_START(KS_loop_3);
    digits[i].Mul(W.b[i], /*matchIndexSet=*/false);
}

{ FHE_NTIMER_START(KS_loop_4);
    this->addPart(digits[i], SKHandle(), /*matchPrimeSet=*/true);
}

  }
}
Exemple #3
0
// Add a constant polynomial
void Ctxt::addConstant(const DoubleCRT& dcrt, double size)
{
  // If the size is not given, we use the default value phi(m)*(ptxtSpace/2)^2
  if (size < 0.0) {
    // WARNING: the following line is written to prevent integer overflow
    size = ((double) context.zMStar.getPhiM()) * ptxtSpace*ptxtSpace /4.0;
  }

  // Scale the constant, then add it to the part that points to one
  long f = (ptxtSpace>2)? rem(context.productOfPrimes(primeSet),ptxtSpace): 1;
  noiseVar += (size*f)*f;

  IndexSet delta = dcrt.getIndexSet() / primeSet; // set minus
  if (f==1 && empty(delta)) { // just add it
    addPart(dcrt, SKHandle(0,1,0));
    return;
  }

  // work with a local copy
  DoubleCRT tmp = dcrt;
  if (!empty(delta)) tmp.removePrimes(delta);
  if (f!=1)          tmp *= f;
  addPart(tmp, SKHandle(0,1,0));
}
Exemple #4
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