Example #1
0
long BKZ_XD(mat_ZZ& BB, mat_ZZ& UU, double delta, 
         long beta, long prune, LLLCheckFct check, long verb)
{
   verbose = verb;
   NumSwaps = 0;
   if (verbose) {
      StartTime = GetTime();
      LastTime = StartTime;
   }


   if (delta < 0.50 || delta >= 1) LogicError("BKZ_XD: bad delta");
   if (beta < 2) LogicError("BKZ_XD: bad block size");

   return BKZ_XD(BB, &UU, to_xdouble(delta), beta, prune, check);
}
Example #2
0
xdouble sqrt(const xdouble& a)
{
   if (a == 0)
      return to_xdouble(0);

   if (a < 0)
      ArithmeticError("xdouble: sqrt of negative number");

   xdouble t;

   if (a.e & 1) {
      t.e = (a.e - 1)/2;
      t.x = sqrt(a.x * NTL_XD_BOUND);
   }
   else {
      t.e = a.e/2;
      t.x = sqrt(a.x);
   }

   t.normalize();

   return t;
}
Example #3
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
Example #4
0
static
long ll_LLL_XD(mat_ZZ& B, mat_ZZ* U, xdouble delta, long deep, 
           LLLCheckFct check, xdouble **B1, xdouble **mu, 
           xdouble *b, xdouble *c,
           long m, long init_k, long &quit)
{
   long n = B.NumCols();

   long i, j, k, Fc1;
   ZZ MU;
   xdouble mu1;

   xdouble t1;
   ZZ T1;
   xdouble *tp;


   NTL_THREAD_LOCAL static xdouble bound = to_xdouble(0);


   if (bound == 0) {
      // we tolerate a 15% loss of precision in computing
      // inner products in ComputeGS.

      bound = 1;
      for (i = 2*long(0.15*NTL_DOUBLE_PRECISION); i > 0; i--) {
         bound = bound * 2;
      }
   }


   xdouble half = to_xdouble(0.5);
   xdouble half_plus_fudge = 0.5 + red_fudge;

   quit = 0;
   k = init_k;

   vec_long st_mem;
   st_mem.SetLength(m+2);
   long *st = st_mem.elts();

   for (i = 1; i < k; i++)
      st[i] = i;

   for (i = k; i <= m+1; i++)
      st[i] = 1;

   UniqueArray<xdouble> buf_store;
   buf_store.SetLength(m+1);
   xdouble *buf = buf_store.get();

   long rst;
   long counter;

   long trigger_index;
   long small_trigger;
   long cnt;

   long max_k = 0;

   double tt;


   while (k <= m) {

      if (k > max_k) {
         max_k = k;
      }

      if (verbose) {
         tt = GetTime();

         if (tt > LastTime + LLLStatusInterval)
            LLLStatus(max_k, tt, m, B);
      }


      if (st[k] == k)
         rst = 1;
      else
         rst = k;

      if (st[k] < st[k+1]) st[k+1] = st[k];
      ComputeGS(B, B1, mu, b, c, k, bound, st[k], buf);
      st[k] = k;

      counter = 0;
      trigger_index = k;
      small_trigger = 0;
      cnt = 0;

      do {
         // size reduction

         counter++;
         if (counter > 10000) {
            cerr << "LLL_XD: warning--possible infinite loop\n";
            counter = 0;
         }


         Fc1 = 0;
   
         for (j = rst-1; j >= 1; j--) {
            t1 = fabs(mu[k][j]);
            if (t1 > half_plus_fudge) {

               if (!Fc1) {
                  if (j > trigger_index ||
                      (j == trigger_index && small_trigger)) {

                     cnt++;

                     if (cnt > 10) {
                        inc_red_fudge();
                        half_plus_fudge = 0.5 + red_fudge;
                        cnt = 0;
                     }
                  }

                  trigger_index = j;
                  small_trigger = (t1 < 4);
               }


               Fc1 = 1;
   
               mu1 = mu[k][j];
               if (mu1 >= 0)
                  mu1 = ceil(mu1-half);
               else
                  mu1 = floor(mu1+half);
   
   
               xdouble *mu_k = mu[k];
               xdouble *mu_j = mu[j];
  
               if (mu1 == 1) {
                  for (i = 1; i <= j-1; i++)
                     mu_k[i] -= mu_j[i];
               }
               else if (mu1 == -1) {
                  for (i = 1; i <= j-1; i++)
                     mu_k[i] += mu_j[i];
               }
               else {
                  for (i = 1; i <= j-1; i++)
                     MulSub(mu_k[i], mu_k[i], mu1, mu_j[i]);
               }
  
               mu_k[j] -= mu1;

               conv(MU, mu1);

               // cout << j << " " << MU << "\n";
   
               RowTransform(B(k), B(j), MU);
               if (U) RowTransform((*U)(k), (*U)(j), MU);
            }
         }

         if (Fc1) {
            for (i = 1; i <= n; i++)
               conv(B1[k][i], B(k, i));
   
            b[k] = InnerProduct(B1[k], B1[k], n);
            ComputeGS(B, B1, mu, b, c, k, bound, 1, buf);
         }
      } while (Fc1);

      if (check && (*check)(B(k))) 
         quit = 1;

      if (b[k] == 0) {
         for (i = k; i < m; i++) {
            // swap i, i+1
            swap(B(i), B(i+1));
            tp = B1[i]; B1[i] = B1[i+1]; B1[i+1] = tp;
            t1 = b[i]; b[i] = b[i+1]; b[i+1] = t1;
            if (U) swap((*U)(i), (*U)(i+1));
         }

         for (i = k; i <= m+1; i++) st[i] = 1;

         m--;
         if (quit) break;
         continue;
      }

      if (quit) break;

      if (deep > 0) {
         // deep insertions
   
         xdouble cc = b[k];
         long l = 1;
         while (l <= k-1 && delta*c[l] <= cc) {
            cc = cc - mu[k][l]*mu[k][l]*c[l];
            l++;
         }
   
         if (l <= k-1 && (l <= deep || k-l <= deep)) {
            // deep insertion at position l
   
            for (i = k; i > l; i--) {
               // swap rows i, i-1
               swap(B(i), B(i-1));
               tp = B1[i]; B1[i] = B1[i-1]; B1[i-1] = tp;
               tp = mu[i]; mu[i] = mu[i-1]; mu[i-1] = tp;
               t1 = b[i]; b[i] = b[i-1]; b[i-1] = t1;
               if (U) swap((*U)(i), (*U)(i-1));
            }
   
            k = l;
            continue;
         }
      } // end deep insertions

      // test LLL reduction condition

      if (k > 1 && delta*c[k-1] > c[k] + mu[k][k-1]*mu[k][k-1]*c[k-1]) {
         // swap rows k, k-1
         swap(B(k), B(k-1));
         tp = B1[k]; B1[k] = B1[k-1]; B1[k-1] = tp;
         tp = mu[k]; mu[k] = mu[k-1]; mu[k-1] = tp;
         t1 = b[k]; b[k] = b[k-1]; b[k-1] = t1;
         if (U) swap((*U)(k), (*U)(k-1));

         k--;
         NumSwaps++;

         // cout << "- " << k << "\n";
      }
      else {
         k++;
         // cout << "+ " << k << "\n";
      }
   }

   if (verbose) {
      LLLStatus(m+1, GetTime(), m, B);
   }


   return m;
}
Example #5
0
      t1 = 0;
      for (i = 1; i <= j-1; i++)
         MulAdd(t1, t1, mu_j[i], buf[i]);

      mu_k[j] = (buf[j] = (s - t1))/c[j];
   }

   s = 0;
   for (j = 1; j <= k-1; j++)
      MulAdd(s, s, mu_k[j], buf[j]);

   c[k] = b[k] - s;
}

NTL_THREAD_LOCAL static xdouble red_fudge = to_xdouble(0);
NTL_THREAD_LOCAL static long log_red = 0;

static void init_red_fudge()
{
   long i;

   log_red = long(0.50*NTL_DOUBLE_PRECISION);
   red_fudge = 1;

   for (i = log_red; i > 0; i--)
      red_fudge = red_fudge*0.5;
}

static void inc_red_fudge()
{
Example #6
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;
}
Example #7
0
      t1 = 0;
      for (i = 1; i <= j-1; i++)
         MulAdd(t1, t1, mu_j[i], buf[i]);

      mu_k[j] = (buf[j] = (s - t1))/c[j];
   }

   s = 0;
   for (j = 1; j <= k-1; j++)
      MulAdd(s, s, mu_k[j], buf[j]);

   c[k] = b[k] - s;
}

static xdouble red_fudge = to_xdouble(0);
static long log_red = 0;

static void init_red_fudge()
{
   long i;

   log_red = long(0.50*NTL_DOUBLE_PRECISION);
   red_fudge = 1;

   for (i = log_red; i > 0; i--)
      red_fudge = red_fudge*0.5;
}

static void inc_red_fudge()
{
Example #8
0
static
long ll_G_LLL_XD(mat_ZZ& B, mat_ZZ* U, xdouble delta, long deep, 
           LLLCheckFct check, xdouble **B1, xdouble **mu, 
           xdouble **aux,
           long m, long init_k, long &quit, GivensCache_XD& cache)
{
   long n = B.NumCols();

   long i, j, k, Fc1;
   ZZ MU;
   xdouble mu1;

   xdouble t1;
   ZZ T1;
   xdouble *tp;


   xdouble half = to_xdouble(0.5);
   xdouble half_plus_fudge = 0.5 + red_fudge;

   quit = 0;
   k = init_k;

   long counter;

   long trigger_index;
   long small_trigger;
   long cnt;

   long max_k = 0;

   double tt;

   cache.flush();

   while (k <= m) {

      if (k > max_k) {
         max_k = k;
      }

      if (verbose) {
         tt = GetTime();

         if (tt > LastTime + LLLStatusInterval)
            G_LLLStatus(max_k, tt, m, B);
      }


      GivensComputeGS(B1, mu, aux, k, n, cache);

      counter = 0;
      trigger_index = k;
      small_trigger = 0;
      cnt = 0;

      do {
         // size reduction

         counter++;
         if (counter > 10000) {
            cerr << "G_LLL_XD: warning--possible infinite loop\n";
            counter = 0;
         }


         Fc1 = 0;
   
         for (j = k-1; j >= 1; j--) {
            t1 = fabs(mu[k][j]);
            if (t1 > half_plus_fudge) {

               if (!Fc1) {
                  if (j > trigger_index ||
                      (j == trigger_index && small_trigger)) {

                     cnt++;

                     if (cnt > 10) {
                        inc_red_fudge();
                        half_plus_fudge = 0.5 + red_fudge;
                        cnt = 0;
                     }
                  }

                  trigger_index = j;
                  small_trigger = (t1 < 4);
               }


               Fc1 = 1;
   
               mu1 = mu[k][j];
               if (mu1 >= 0)
                  mu1 = ceil(mu1-half);
               else
                  mu1 = floor(mu1+half);
   
   
               xdouble *mu_k = mu[k];
               xdouble *mu_j = mu[j];
  
               if (mu1 == 1) {
                  for (i = 1; i <= j-1; i++)
                     mu_k[i] -= mu_j[i];
               }
               else if (mu1 == -1) {
                  for (i = 1; i <= j-1; i++)
                     mu_k[i] += mu_j[i];
               }
               else {
                  for (i = 1; i <= j-1; i++)
                     MulSub(mu_k[i], mu_k[i], mu1, mu_j[i]);
               }
  
               mu_k[j] -= mu1;

               conv(MU, mu1);

               // cout << j << " " << MU << "\n";
   
               RowTransform(B(k), B(j), MU);
               if (U) RowTransform((*U)(k), (*U)(j), MU);
            }
         }

         if (Fc1) {
            for (i = 1; i <= n; i++)
               conv(B1[k][i], B(k, i));
            cache.touch();
            GivensComputeGS(B1, mu, aux, k, n, cache);
         }
      } while (Fc1);

      if (check && (*check)(B(k))) 
         quit = 1;

      if (IsZero(B(k))) {
         for (i = k; i < m; i++) {
            // swap i, i+1
            swap(B(i), B(i+1));
            tp = B1[i]; B1[i] = B1[i+1]; B1[i+1] = tp;
            if (U) swap((*U)(i), (*U)(i+1));
         }

         cache.flush();

         m--;
         if (quit) break;
         continue;
      }

      if (quit) break;

      if (deep > 0) {
         // deep insertions
   
         Error("sorry...deep insertions not implemented");
      } // end deep insertions

      // test G_LLL reduction condition

      if (k > 1 && 
         (delta - mu[k][k-1]*mu[k][k-1])*(mu[k-1][k-1])*(mu[k-1][k-1]) >
         (mu[k][k])*(mu[k][k])) {

         // swap rows k, k-1
         swap(B(k), B(k-1));
         tp = B1[k]; B1[k] = B1[k-1]; B1[k-1] = tp;
         if (U) swap((*U)(k), (*U)(k-1));

         cache.swap();

         k--;
         NumSwaps++;

         // cout << "- " << k << "\n";
      }
      else {
         cache.incr();
         k++;
         // cout << "+ " << k << "\n";
      }
   }

   if (verbose) {
      G_LLLStatus(m+1, GetTime(), m, B);
   }


   return m;
}
Example #9
0
int main(int argc, char *argv[]) 
{
  if (argc<2) {
    cout << "\nUsage: " << argv[0] << " L [c=2 w=64 k=80 d=1]" << endl;
    cout << "  L is the number of levels\n";
    cout << "  optional c is number of columns in the key-switching matrices (default=2)\n";
    cout << "  optional w is Hamming weight of the secret key (default=64)\n";
    cout << "  optional k is the security parameter (default=80)\n";
    cout << "  optional d specifies GF(2^d) arithmetic (default=1, must be <=16)\n";
    //    cout << "  k is the security parameter\n";
    //    cout << "  m determines the ring mod Phi_m(X)" << endl;
    cout << endl;
    exit(0);
  }
  cout.unsetf(ios::floatfield);
  cout.precision(4);

  long L = atoi(argv[1]);
  long c = 2;
  long w = 64;
  long k = 80;
  long d = 1;
  if (argc>2) c = atoi(argv[2]);
  if (argc>3) w = atoi(argv[3]);
  if (argc>4) k = atoi(argv[4]);
  if (argc>5) d = atoi(argv[5]);

  if (d>16) Error("d cannot be larger than 16\n");

  cout << "\nTesting FHE with parameters L="<<L
       << ", c="<<c<<", w="<<w<<", k="<<k<<", d="<<d<< endl;

  // get a lower-bound on the parameter N=phi(m):
  // 1. Empirically, we use ~20-bit small primes in the modulus chain (the main
  //    constraints is that 2m must divide p-1 for every prime p). The first
  //    prime is larger, a 40-bit prime. (If this is a 32-bit machine then we
  //    use two 20-bit primes instead.)
  // 2. With L levels, the largest modulus for "fresh ciphertexts" has size
  //          q0 ~ p0 * p^{L} ~ 2^{40+20L}
  // 3. We break each ciphertext into upto c digits, do each digit is as large
  //    as    D=2^{(40+20L)/c}
  // 4. The added noise variance term from the key-switching operation is
  //    c*N*sigma^2*D^2, and this must be mod-switched down to w*N (so it is
  //    on part with the added noise from modulus-switching). Hence the ratio
  //    P that we use for mod-switching must satisfy c*N*sigma^2*D^2/P^2<w*N,
  //    or    P > sqrt(c/w) * sigma * 2^{(40+20L)/c}
  // 5. With this extra P factor, the key-switching matrices are defined
  //    relative to a modulus of size
  //          Q0 = q0*P ~ sqrt{c/w} sigma 2^{(40+20L)(1+1/c)}
  // 6. To get k-bit security we need N>log(Q0/sigma)(k+110)/7.2, i.e. roughly
  //          N > (40+20L)(1+1/c)(k+110) / 7.2

  long ptxtSpace = 2;
  double cc = 1.0+(1.0/(double)c);
  long N = (long) ceil((pSize*L+p0Size)*cc*(k+110)/7.2);
  cout << "  bounding phi(m) > " << N << endl;

#if 0  // A small m for debugging purposes
  long m = 15;
#else
  // pre-computed values of [phi(m),m,d]
  long ms[][4] = {
    //phi(m)  m  ord(2) c_m*1000
    { 1176,  1247, 28,  3736},
    { 1936,  2047, 11,  3870},
    { 2880,  3133, 24,  3254},
    { 4096,  4369, 16,  3422},
    { 5292,  5461, 14,  4160},
    { 5760,  8435, 24,  8935},
    { 8190,  8191, 13,  1273},
    {10584, 16383, 14,  8358},
    {10752, 11441, 48,  3607},
    {12000, 13981, 20,  2467},
    {11520, 15665, 24, 14916},
    {14112, 18415, 28, 11278},
    {15004, 15709, 22,  3867},
    {15360, 20485, 24, 12767},
 // {16384, 21845, 16, 12798},
    {17208 ,21931, 24, 18387},
    {18000, 18631, 25,  4208},
    {18816, 24295, 28, 16360},
    {19200, 21607, 40, 35633},
    {21168, 27305, 28, 15407},
    {23040, 23377, 48,  5292},
    {24576, 24929, 48,  5612},
    {27000, 32767, 15, 20021},
    {31104, 31609, 71,  5149},
    {42336, 42799, 21,  5952},
    {46080, 53261, 24, 33409},
    {49140, 57337, 39,  2608},
    {51840, 59527, 72, 21128},
    {61680, 61681, 40,  1273},
    {65536, 65537, 32,  1273},
    {75264, 82603, 56, 36484},
    {84672, 92837, 56, 38520}
  };

#if 0

  for (long i = 0; i < 25; i++) {
    long m = ms[i][1];
    PAlgebra alg(m);
    alg.printout();
    cout << "\n";
    // compute phi(m) directly
    long phim = 0;
    for (long j = 0; j < m; j++)
      if (GCD(j, m) == 1) phim++;

    if (phim != alg.phiM()) cout << "ERROR\n";
  }

  exit(0);


#endif



  // find the first m satisfying phi(m)>=N and d | ord(2) in Z_m^*
  long m = 0;
  for (unsigned i=0; i<sizeof(ms)/sizeof(long[3]); i++) 
    if (ms[i][0]>=N && (ms[i][2] % d) == 0) {
      m = ms[i][1];
      c_m = 0.001 * (double) ms[i][3];
      break;
    }
  if (m==0) Error("Cannot support this L,d combination");
#endif
  //  m = 257;
  FHEcontext context(m);
#if 0
  context.stdev = to_xdouble(0.5); // very low error
#endif
  activeContext = &context; // Mark this as the "current" context

  context.zMstar.printout();
  cout << endl;


  // Set the modulus chain

#if 1
  // The first 1-2 primes of total p0size bits
  #if (NTL_SP_NBITS > p0Size)
    AddPrimesByNumber(context, 1, 1UL<<p0Size); // add a single prime
  #else
    AddPrimesByNumber(context, 2, 1UL<<(p0Size/2)); // add two primes
  #endif
#endif

  // The next L primes, as small as possible
  AddPrimesByNumber(context, L);

  ZZ productOfCtxtPrimes = context.productOfPrimes(context.ctxtPrimes);
  double productSize = context.logOfProduct(context.ctxtPrimes);

  // might as well test that the answer is roughly correct
  cout << "  context.logOfProduct(...)-log(context.productOfPrimes(...)) = "
       << productSize-log(productOfCtxtPrimes) << endl;

  // calculate the size of the digits

  context.digits.resize(c);
  IndexSet s1;
#if 0
  for (long i=0; i<c-1; i++) context.digits[i] = IndexSet(i,i);
  context.digits[c-1] = context.ctxtPrimes / IndexSet(0,c-2);
  AddPrimesByNumber(context, 2, 1, true);
#else
  double sizeSoFar = 0.0;
  double maxDigitSize = 0.0;
  if (c>1) {   // break ciphetext into a few digits
    double dsize = productSize/c;  // initial estimate
    double target = dsize-(pSize/3.0);
    long idx = context.ctxtPrimes.first();
    for (long i=0; i<c-1; i++) { // compute next digit
      IndexSet s;
      while (idx <= context.ctxtPrimes.last() && sizeSoFar < target) {
        s.insert(idx);
	sizeSoFar += log((double)context.ithPrime(idx));
	idx = context.ctxtPrimes.next(idx);
      }
      context.digits[i] = s;
      s1.insert(s);
      double thisDigitSize = context.logOfProduct(s);
      if (maxDigitSize < thisDigitSize) maxDigitSize = thisDigitSize;
      cout << "  digit #"<<i+1<< " " <<s << ": size " << thisDigitSize << endl;
      target += dsize;
    }
    IndexSet s = context.ctxtPrimes / s1; // all the remaining primes
    context.digits[c-1] = s;
    double thisDigitSize = context.logOfProduct(s);
    if (maxDigitSize < thisDigitSize) maxDigitSize = thisDigitSize;
    cout << "  digit #"<<c<< " " <<s << ": size " << thisDigitSize << endl;
  }
  else { 
    maxDigitSize = context.logOfProduct(context.ctxtPrimes);
    context.digits[0] = context.ctxtPrimes;
  }

  // Add primes to the chain for the P factor of key-switching
  double sizeOfSpecialPrimes 
    = maxDigitSize + log(c/(double)w)/2 + log(context.stdev *2);

  AddPrimesBySize(context, sizeOfSpecialPrimes, true);
#endif

  cout << "* ctxtPrimes: " << context.ctxtPrimes 
       << ", log(q0)=" << context.logOfProduct(context.ctxtPrimes) << endl;
  cout << "* specialPrimes: " << context.specialPrimes
       << ", log(P)=" << context.logOfProduct(context.specialPrimes) << endl;

  for (long i=0; i<context.numPrimes(); i++) {
    cout << "  modulus #" << i << " " << context.ithPrime(i) << endl;
  }
  cout << endl;

  setTimersOn();
  const ZZX& PhimX = context.zMstar.PhimX(); // The polynomial Phi_m(X)
  long phim = context.zMstar.phiM();         // The integer phi(m)
  FHESecKey secretKey(context);
  const FHEPubKey& publicKey = secretKey;

#if 0 // Debug mode: use sk=1,2
  DoubleCRT newSk(to_ZZX(2), context);
  long id1 = secretKey.ImportSecKey(newSk, 64, ptxtSpace);
  newSk -= 1;
  long id2 = secretKey.ImportSecKey(newSk, 64, ptxtSpace);
#else
  long id1 = secretKey.GenSecKey(w,ptxtSpace); // A Hamming-weight-w secret key
  long id2 = secretKey.GenSecKey(w,ptxtSpace); // A second Hamming-weight-w secret key
#endif

  ZZX zero = to_ZZX(0);
//  Ctxt zeroCtxt(publicKey);

  /******************************************************************/
  /**                      TESTS BEGIN HERE                       ***/
  /******************************************************************/


  cout << "ptxtSpace = " << ptxtSpace << endl;

  GF2X G;          // G is the AES polynomial, G(X)= X^8 +X^4 +X^3 +X +1
  SetCoeff(G,8); SetCoeff(G,4); SetCoeff(G,3); SetCoeff(G,1); SetCoeff(G,0);
  GF2X X;
  SetX(X);
  
#if 1
  // code for rotations...

  {
    GF2X::HexOutput = 1;
    
    const PAlgebra& al = context.zMstar;
    const PAlgebraModTwo& al2 = context.modTwo;

    long ngens = al.numOfGens();
    long nslots = al.NSlots();
    DoubleCRT tmp(context);

    vector< vector< DoubleCRT > > maskTable;

    maskTable.resize(ngens);
    for (long i = 0; i < ngens; i++) {
      if (i==0 && al.SameOrd(i)) continue;
      long ord = al.OrderOf(i);
      maskTable[i].resize(ord+1, tmp);
      for (long j = 0; j <= ord; j++) {
        // initialize the mask that is 1 whenever
        // the ith coordinate is at least j

        vector<GF2X> maps, alphas, betas;

        al2.mapToSlots(maps, G); // Change G to X to get bits in the slots
        alphas.resize(nslots);

        for (long k = 0; k < nslots; k++) 
          if (coordinate(al, i, k) >= j)
               alphas[k] = 1;
          else alphas[k] = 0;

       GF2X ptxt;
       al2.embedInSlots(ptxt, alphas, maps);

       // Sanity-check, make sure that encode/decode works as expected
       al2.decodePlaintext(betas, ptxt, G, maps);
       for (long k = 0; k < nslots; k++) {
	 if (alphas[k] != betas[k]) {
	   cout << " Mask computation failed, i="<<i<<", j="<<j<<"\n";
	   return 0;
	 }
       }
       maskTable[i][j] = to_ZZX(ptxt);
      }
    }

  vector<GF2X> maps;
  al2.mapToSlots(maps, G);

  vector<GF2X> alphas(nslots);
  for (long i=0; i < nslots; i++) 
    random(alphas[i], 8); // random degree-7 polynomial mod 2

  for (long amt = 0; amt < 20; amt++) {

    cout << ".";

     GF2X ptxt;
     al2.embedInSlots(ptxt, alphas, maps);

     DoubleCRT pp(context);
     pp = to_ZZX(ptxt);

     rotate(pp, amt, maskTable);

     GF2X ptxt1 = to_GF2X(to_ZZX(pp));

     vector<GF2X> betas;
     al2.decodePlaintext(betas, ptxt1, G, maps);

     for (long i = 0; i < nslots; i++) {
       if (alphas[i] != betas[(i+amt)%nslots]) {
	 cout << " amt="<<amt<<" oops\n";
          return 0;
       }
     }
   }

   cout << "\n";

#if 0
  long ord0 = al.OrderOf(0);

  for (long i = 0; i < nslots; i++) {
    cout << alphas[i] << " ";
    if ((i+1) % (nslots/ord0) == 0) cout << "\n";
  }
 
  cout << "\n\n";
  cout << betas.size() << "\n";

  for (long i = 0; i < nslots; i++) {
    cout << betas[i] << " ";
    if ((i+1) % (nslots/ord0) == 0) cout << "\n";
  }
#endif


  return 0;
  

  }


#endif

  // an initial sanity check on noise estimates,
  // comparing the estimated variance to the actual average
  cout << "pk:"; checkCiphertext(publicKey.pubEncrKey, zero, secretKey);

  ZZX ptxt[6]; // first four are plaintext, last two are constants
  std::vector<Ctxt> ctxt(4, Ctxt(publicKey));

  // Initialize the plaintext and constants to random 0-1 polynomials
  for (size_t j=0; j<6; j++) {
    ptxt[j].rep.SetLength(phim);
    for (long i = 0; i < phim; i++)
      ptxt[j].rep[i] = RandomBnd(ptxtSpace);
    ptxt[j].normalize();

    if (j<4) { 
      publicKey.Encrypt(ctxt[j], ptxt[j], ptxtSpace);
      cout << "c"<<j<<":"; checkCiphertext(ctxt[j], ptxt[j], secretKey);
    }
  }

  // perform upto 2L levels of computation, each level computing:
  //    1. c0 += c1
  //    2. c1 *= c2            // L1' = max(L1,L2)+1
  //    3. c1.reLinearlize
  //    4. c2 *= p4
  //    5. c2.automorph(k)     // k is the first generator of Zm^* /(2)
  //    6. c2.reLinearlize
  //    7. c3 += p5
  //    8. c3 *= c0            // L3' = max(L3,L0,L1)+1
  //    9. c2 *= c3            // L2' = max(L2,L0+1,L1+1,L3+1)+1
  //   10. c0 *= c0            // L0' = max(L0,L1)+1
  //   11. c0.reLinearlize
  //   12. c2.reLinearlize
  //   13. c3.reLinearlize
  //
  // The levels of the four ciphertexts behave as follows:
  // 0, 0, 0, 0  =>  1, 1, 2, 1  =>  2, 3, 3, 2
  //             =>  4, 4, 5, 4  =>  5, 6, 6, 5
  //             =>  7, 7, 8, 7  =>  8,,9, 9, 10  => [...]
  //
  // We perform the same operations on the plaintext, and after each operation
  // we check that decryption still works, and print the curretn modulus and
  // noise estimate. We stop when we get the first decryption error, or when
  // we reach 2L levels (which really should not happen).

  zz_pContext zzpc;
  zz_p::init(ptxtSpace);
  zzpc.save();
  const zz_pXModulus F = to_zz_pX(PhimX);
  long g = context.zMstar.ZmStarGen(0); // the first generator in Zm*
  zz_pX x2g(g, 1);
  zz_pX p2;

  // generate a key-switching matrix from s(X^g) to s(X)
  secretKey.GenKeySWmatrix(/*powerOfS= */  1,
			   /*powerOfX= */  g,
			   0, 0,
			   /*ptxtSpace=*/  ptxtSpace);

  // generate a key-switching matrix from s^2 to s
  secretKey.GenKeySWmatrix(/*powerOfS= */  2,
			   /*powerOfX= */  1,
			   0, 0,
			   /*ptxtSpace=*/  ptxtSpace);

  // generate a key-switching matrix from s^3 to s
  secretKey.GenKeySWmatrix(/*powerOfS= */  3,
			   /*powerOfX= */  1,
			   0, 0,
			   /*ptxtSpace=*/  ptxtSpace);

  for (long lvl=0; lvl<2*L; lvl++) {
    cout << "=======================================================\n";
    ctxt[0] += ctxt[1];
    ptxt[0] += ptxt[1];
    PolyRed(ptxt[0], ptxtSpace, true);
    cout << "c0+=c1:  "; checkCiphertext(ctxt[0], ptxt[0], secretKey);

    ctxt[1].multiplyBy(ctxt[2]);
    ptxt[1] = (ptxt[1] * ptxt[2]) % PhimX;
    PolyRed(ptxt[1], ptxtSpace, true);
    cout << "c1*=c2:  "; checkCiphertext(ctxt[1], ptxt[1], secretKey);

    ctxt[2].multByConstant(ptxt[4]);
    ptxt[2] = (ptxt[2] * ptxt[4]) % PhimX;
    PolyRed(ptxt[2], ptxtSpace, true);
    cout <<  "c2*=p4:  "; checkCiphertext(ctxt[2], ptxt[2], secretKey);

    ctxt[2] >>= g;
    zzpc.restore();
    p2 = to_zz_pX(ptxt[2]);
    CompMod(p2, p2, x2g, F);
    ptxt[2] = to_ZZX(p2);
    cout << "c2>>="<<g<<":"; checkCiphertext(ctxt[2], ptxt[2], secretKey);

    ctxt[2].reLinearize();
    cout << "c2.relin:"; checkCiphertext(ctxt[2], ptxt[2], secretKey);

    ctxt[3].addConstant(ptxt[5]);
    ptxt[3] += ptxt[5];
    PolyRed(ptxt[3], ptxtSpace, true);
    cout << "c3+=p5:  "; checkCiphertext(ctxt[3], ptxt[3], secretKey);

    ctxt[3].multiplyBy(ctxt[0]);
    ptxt[3] = (ptxt[3] * ptxt[0]) % PhimX;
    PolyRed(ptxt[3], ptxtSpace, true);
    cout << "c3*=c0:  ";    checkCiphertext(ctxt[3], ptxt[3], secretKey);

    ctxt[0].square();
    ptxt[0] = (ptxt[0] * ptxt[0]) % PhimX;
    PolyRed(ptxt[0], ptxtSpace, true);
    cout << "c0*=c0:  ";    checkCiphertext(ctxt[0], ptxt[0], secretKey);

    ctxt[2].multiplyBy(ctxt[3]);
    ptxt[2] = (ptxt[2] * ptxt[3]) % PhimX;
    PolyRed(ptxt[2], ptxtSpace, true);
    cout << "c2*=c3:  ";    checkCiphertext(ctxt[2], ptxt[2], secretKey);
  }
  /******************************************************************/
  /**                       TESTS END HERE                        ***/
  /******************************************************************/
  cout << endl;
  return 0;
}
Example #10
0
      t1 = 0;
      for (i = 1; i <= j-1; i++)
         MulAdd(t1, t1, mu_j[i], buf[i]);

      mu_k[j] = (buf[j] = (s - t1))/c[j];
   }

   s = 0;
   for (j = 1; j <= k-1; j++)
      MulAdd(s, s, mu_k[j], buf[j]);

   c[k] = b[k] - s;
}

NTL_TLS_GLOBAL_DECL_INIT(xdouble, red_fudge, (to_xdouble(0)))


static NTL_CHEAP_THREAD_LOCAL long log_red = 0;

static void init_red_fudge()
{
   NTL_TLS_GLOBAL_ACCESS(red_fudge);

   long i;

   log_red = long(0.50*NTL_DOUBLE_PRECISION);
   red_fudge = 1;

   for (i = log_red; i > 0; i--)
      red_fudge = red_fudge*0.5;