// Create a tensor product of c1,c2. It is assumed that *this,c1,c2 // are defined relative to the same set of primes and plaintext space, // and that *this DOES NOT point to the same object as c1,c2 void Ctxt::tensorProduct(const Ctxt& c1, const Ctxt& c2) { // c1,c2 may be scaled, so multiply by the inverse scalar if needed long f = 1; if (c1.ptxtSpace>2) f = rem(context.productOfPrimes(c1.primeSet),c1.ptxtSpace); if (f!=1) f = InvMod(f,c1.ptxtSpace); clear(); // clear *this, before we start adding things to it primeSet = c1.primeSet; // set the correct prime-set before we begin // The actual tensoring CtxtPart tmpPart(context, IndexSet::emptySet()); // a scratch CtxtPart for (size_t i=0; i<c1.parts.size(); i++) { CtxtPart thisPart = c1.parts[i]; if (f!=1) thisPart *= f; for (size_t j=0; j<c2.parts.size(); j++) { tmpPart = c2.parts[j]; // What secret key will the product point to? if (!tmpPart.skHandle.mul(thisPart.skHandle, tmpPart.skHandle)) Error("Ctxt::tensorProduct: cannot multiply secret-key handles"); tmpPart *= thisPart; // The element of the tensor product // Check if we already have a part relative to this secret-key handle long k = getPartIndexByHandle(tmpPart.skHandle); if (k >= 0) // found a matching part parts[k] += tmpPart; else parts.push_back(tmpPart); } } /* Compute the noise estimate as c1.noiseVar * c2.noiseVar * factor * where the factor depends on the handles of c1,c2. Specifically, * if the largest powerOfS in c1,c2 are n1,n2, respectively, then we * have factor = ((n1+n2) choose n2). */ long n1=0, n2=0; for (size_t i=0; i<c1.parts.size(); i++) // get largest powerOfS in c1 if (c1.parts[i].skHandle.getPowerOfS() > n1) n1 = c1.parts[i].skHandle.getPowerOfS(); for (size_t i=0; i<c2.parts.size(); i++) // get largest powerOfS in c2 if (c2.parts[i].skHandle.getPowerOfS() > n2) n2 = c2.parts[i].skHandle.getPowerOfS(); // compute ((n1+n2) choose n2) long factor = 1; for (long i=n1+1; i<=n1+n2; i++) factor *= i; for (long i=n2 ; i>1 ; i--) factor /= i; noiseVar = c1.noiseVar * c2.noiseVar * factor * context.zMStar.get_cM(); if (f!=1) { // WARNING: the following line is written just so to prevent overflow noiseVar = (noiseVar*f)*f; // because every product was scaled by f } }
// Sets the prime defining the field for the curve and stores certain values void Icart::setPrime(ZZ* p) { //ZZ_p::init(*p); // Icart hash function uses 1/3 root, which is equivalent to (2p-1)/3 exp = MulMod( SubMod( MulMod(ZZ(2), *p, *p), ZZ(1), *p), InvMod(ZZ(3),*p), *p); // Store inverse values to be used later ts = inv(ZZ_p(27)); th = inv(ZZ_p(3)); }
void InitFFTPrimeInfo(FFTPrimeInfo& info, long q, long w, long bigtab) { double qinv = 1/((double) q); long mr = CalcMaxRoot(q); info.q = q; info.qinv = qinv; info.zz_p_context = 0; info.RootTable.SetLength(mr+1); info.RootInvTable.SetLength(mr+1); info.TwoInvTable.SetLength(mr+1); info.TwoInvPreconTable.SetLength(mr+1); long *rt = &info.RootTable[0]; long *rit = &info.RootInvTable[0]; long *tit = &info.TwoInvTable[0]; mulmod_precon_t *tipt = &info.TwoInvPreconTable[0]; long j; long t; rt[mr] = w; for (j = mr-1; j >= 0; j--) rt[j] = MulMod(rt[j+1], rt[j+1], q); rit[mr] = InvMod(w, q); for (j = mr-1; j >= 0; j--) rit[j] = MulMod(rit[j+1], rit[j+1], q); t = InvMod(2, q); tit[0] = 1; for (j = 1; j <= mr; j++) tit[j] = MulMod(tit[j-1], t, q); for (j = 0; j <= mr; j++) tipt[j] = PrepMulModPrecon(tit[j], q, qinv); info.bigtab = bigtab; }
NTL_CLIENT #include <algorithm> // defines count(...), min(...) #include <iostream> #include "NumbTh.h" #include "PAlgebra.h" // Generate the representation of Z[X]/(Phi_m(X),2) for the odd integer m void PAlgebraModTwo::init(unsigned m) { if (m == zmStar.M()) return; // nothign to do ((PAlgebra&)zmStar).init(m); // initialize the structure of (Z/mZ)*, if needed if (zmStar.M()==0 || zmStar.NSlots()==0) return; // error in zmStar unsigned nSlots = zmStar.NSlots(); // Next compute the factors Ft of Phi_m(X) mod 2, for all t \in T // GF2X PhimXmod = to_GF2X(zmStar.PhimX()); // Phi_m(X) mod 2 PhimXmod = to_GF2X(zmStar.PhimX()); // Phi_m(X) mod 2 EDF(factors, PhimXmod, zmStar.OrdTwo()); // equal-degree factorization // It is left to order the factors according to their representatives GF2XModulus F1(factors[0]); // We arbitrarily choose factors[0] as F1 for (unsigned i=1; i<nSlots; i++) { unsigned t = zmStar.ith_rep(i); // Ft is minimal polynomial of x^{1/t} mod F1 unsigned tInv = rep(inv(to_zz_p(t))); // tInv = t^{-1} mod m GF2X X2tInv = PowerXMod(tInv,F1); // X2tInv = X^{1/t} mod F1 IrredPolyMod(factors[i], X2tInv, F1); } /* Debugging sanity-check #1: we should have Ft= GCD(F1(X^t),Phi_m(X)) GF2XModulus Pm2(PhimXmod); for (i=1; i<nSlots; i++) { unsigned t = T[i]; GF2X X2t = PowerXMod(t,PhimXmod); // X2t = X^t mod Phi_m(X) GF2X Ft = GCD(CompMod(F1,X2t,Pm2),Pm2); if (Ft != factors[i]) { cout << "Ft != F1(X^t) mod Phi_m(X), t=" << t << endl; exit(0); } }*******************************************************************/ // Compute the CRT coefficients for the Ft's crtCoeffs.SetLength(nSlots); for (unsigned i=0; i<nSlots; i++) { GF2X te = PhimXmod / factors[i]; // \prod_{j\ne i} Fj te %= factors[i]; // \prod_{j\ne i} Fj mod Fi InvMod(crtCoeffs[i], te, factors[i]);// \prod_{j\ne i} Fj^{-1} mod Fi } }
// divVec[d] = m/p_d^{e_d}, powVec[d] = p^{e_d} // computes invVec[d] = divVec[d]^{-1} mod powVec[d] void computeInvVec(Vec<long>& invVec, const Vec<long>& divVec, const Vec<long>& powVec) { long k = divVec.length(); invVec.SetLength(k); for (long d = 0; d < k; d++) { long t1 = divVec[d] % powVec[d]; long t2 = InvMod(t1, powVec[d]); invVec[d] = t2; } }
ZZ ASTDiv::eval(Environment &env) { pair<VarInfo,VarInfo> p = getTypes(env); VarInfo leftInfo = p.first; VarInfo rightInfo = p.second; // XXX: this will cause a problem if we ever try to use // it during constant propagation/substitution const Group* lGroup = env.groups.at(leftInfo.group); const Group* rGroup = env.groups.at(rightInfo.group); const Group* retGroup = (lGroup != 0 ? lGroup : rGroup); // integers can just be divided if ((lGroup == 0 && rGroup == 0) || (leftInfo.type == VarInfo::MODULUS || rightInfo.type == VarInfo::MODULUS)) { return lhs->eval(env) / rhs->eval(env); } else if (leftInfo.type == VarInfo::EXPONENT || rightInfo.type == VarInfo::EXPONENT) { // want to get LHS * inv(RHS) // this only works if we are in a group with known order if (retGroup->getType() == Group::TYPE_PRIME) { ZZ ord = retGroup->getOrder(); ZZ right = InvMod(rhs->eval(env), ord); return lhs->eval(env) * right; // in an RSA group, only permit computing 1/x if the caller // knows the order of the group } else if(retGroup->getType() == Group::TYPE_RSA && (retGroup->getOrder() != 0)) { ZZ ord = retGroup->getOrder(); ZZ right = InvMod(rhs->eval(env), ord); return lhs->eval(env) * right; } else { throw CashException(CashException::CE_PARSE_ERROR, "That operation is not permitted in an RSA group"); } } else { assert(retGroup); ZZ mod = retGroup->getModulus(); return MulMod(lhs->eval(env), InvMod(rhs->eval(env), mod), mod); } }
ZZ BankTool::identifyDoubleSpender(const Coin& coin1, const ZZ &tPrime2, const ZZ& rValue2) const { // Should probably check that the R values are different ZZ mod = coin1.getCashGroup()->getModulus(); ZZ order = coin1.getCashGroup()->getOrder(); ZZ t1 = coin1.getTPrime(); ZZ t2 = tPrime2; ZZ r1 = coin1.getR(); ZZ r2 = rValue2; if (r2 > r1) { NTL::swap(r2, r1); NTL::swap(t2, t1); } ZZ exp = InvMod(r1 - r2, order); ZZ num = PowerMod(t2, r1, mod); ZZ denom = InvMod(PowerMod(t1, r2, mod), mod); ZZ base = MulMod(num, denom, mod); ZZ publicKeyUser = PowerMod(base, exp, mod); return publicKeyUser; }
long solve(int *S, int *R) { // start = S, each = R for (int i = 0; i < m; ++i) { visited[i] = false; inv[S[i]] = i; } long ret = 0, MOD = 1; for (int i = 0; i < m; ++i) { if (visited[i]) continue; // print("--- loop ---\n"); int loop = 0; for (int j = i; !visited[j]; j = R[j]) { // print("{} ", j); visited[j] = true; vs[loop] = S[j] % 2; vr[loop] = j % 2; ++loop; } // print("\n"); TIME += loop; int mod, rem; tie(mod, rem) = match(loop); // for (int i = 0; i < loop; ++i) { // print("{} {}\n", vs[i], vr[i]); // } // print("mod = {}, rem = {}\n", mod, rem); if (mod == 0) { return 1e18; } long g = GCD(MOD, mod); if (ret % g != rem % g) { return 1e18; } // print("mod = {}, rem = {}\n", mod, rem); MOD /= g, mod /= g; long t = InvMod(MOD, mod) * ((rem - ret) / g % mod + mod) % mod; // print("t = {}\n", t); assert((t * MOD * g + ret) % (mod * g) == rem); ret = t * MOD * g + ret; MOD = MOD * mod * g; // print("MOD = {}, REM = {}\n", MOD, ret); // print("loop: {}\n", n); } return ret; }
// Division by constant // FIXME: this is not alias friendly SingleCRT& SingleCRT::operator/=(const ZZ &num) { const IndexSet& s = map.getIndexSet(); ZZ pi, n; for (long i = s.first(); i <= s.last(); i = s.next(i)) { pi = to_ZZ(context.ithPrime(i)); rem(n,num,pi); InvMod(n,n,pi); // n = num^{-1} mod pi vec_ZZ& vp = map[i].rep; for (long j=0; j<vp.length(); j++) MulMod(vp[j], vp[j], n, pi); map[i].normalize(); } return *this; }
// Процедура проверки подписи void verifysign (ZZ &R, ZZ &r, ZZ &s, ZZ &e, Qxy &Q, Qxy &P, ZZ &q ) { ZZ v, z1, z2; Qxy C; ZZ e1; if (InvModStatus( v, e, q )) { cout << "\nError in signature\n"; goto end; } v = InvMod(e,q); z1 = (s*v)%q; z2 = (-r*v)%q; C = P*z1 + Q*z2; R = (conv<ZZ>(C.putx()))%q; cout << "\nv (dec) = \n" << v << endl; cout << "\nv (hex) = \n"; show_dec_in_hex (v, L); cout << endl; cout << "\nz1 (dec) = \n" << z1 << endl; cout << "\nz1 (hex) = \n"; show_dec_in_hex (z1, L); cout << endl; cout << "\nz2 (dec) = \n" << z2 << endl; cout << "\nz2 (hex) = \n"; show_dec_in_hex (z2, L); cout << endl; cout << "\nPoint C:\n"; C.putQxy(); cout << "\nR (dec) = \n" << R << endl; cout << "\nR (hex) = \n"; show_dec_in_hex (R, L); cout << endl; end: if ( r == R ) cout << "\nr = R\nSignature is OK"; else cout << "\nSignature is FAILD"; cout << endl; }
// 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; }
// Divide a cipehrtext by p, for plaintext space p^r, r>1. It is assumed // that the ciphertext encrypts a polynomial which is zero mod p. If this // is not the case then the result will not be a valid ciphertext anymore. // As a side-effect, the plaintext space is reduced from p^r to p^{r-1}. void Ctxt::divideByP() { // Special case: if *this is empty then do nothing if (this->isEmpty()) return; long p = getContext().zMStar.getP(); assert (ptxtSpace>p); // multiply all the parts by p^{-1} mod Q (Q=productOfPrimes) ZZ pInverse, Q; getContext().productOfPrimes(Q, getPrimeSet()); InvMod(pInverse, conv<ZZ>(p), Q); for (size_t i=0; i<parts.size(); i++) parts[i] *= pInverse; noiseVar /= (p * (double)p); // noise is reduced by a p factor ptxtSpace /= p; // and so is the plaintext space }
// generate all matrices of the form s(X^{g^i})->s(X) for generators g of // Zm* /<2> and i<ord(g). If g has different orders in Zm* and Zm* /<2> // then generate also matrices of the form s(X^{g^{-i}})->s(X) void add1DMatrices(FHESecKey& sKey, long keyID) { const FHEcontext &context = sKey.getContext(); long m = context.zMStar.getM(); // key-switching matrices for the automorphisms for (long i = 0; i < (long)context.zMStar.numOfGens(); i++) { for (long j = 1; j < (long)context.zMStar.OrderOf(i); j++) { long val = PowerMod(context.zMStar.ZmStarGen(i), j, m); // val = g^j // From s(X^val) to s(X) sKey.GenKeySWmatrix(1, val, keyID, keyID); if (!context.zMStar.SameOrd(i)) // also from s(X^{1/val}) to s(X) sKey.GenKeySWmatrix(1, InvMod(val,m), keyID, keyID); } } sKey.setKeySwitchMap(); // re-compute the key-switching map }
// Apply F(X)->F(X^k) followed by re-liearization. The automorphism is possibly // evaluated via a sequence of steps, to ensure that we can re-linearize the // result of every step. void Ctxt::smartAutomorph(long k) { FHE_TIMER_START; // A hack: record this automorphism rather than actually performing it if (isSetAutomorphVals()) { // defined in NumbTh.h recordAutomorphVal(k); return; } // Special case: if *this is empty then do nothing if (this->isEmpty()) return; // Sanity check: verify that k \in Zm* long m = context.zMStar.getM(); k = mcMod(k, m); assert (context.zMStar.inZmStar(k)); long keyID=getKeyID(); if (!pubKey.isReachable(k,keyID)) {// must have key-switching matrices for it throw std::logic_error("no key-switching matrices for k="+std::to_string(k) + ", keyID="+std::to_string(keyID)); } if (!inCanonicalForm(keyID)) { // Re-linearize the input, if needed reLinearize(keyID); assert (inCanonicalForm(keyID)); // ensure that re-linearization succeeded } while (k != 1) { const KeySwitch& matrix = pubKey.getNextKSWmatrix(k,keyID); long amt = matrix.fromKey.getPowerOfX(); // A hack: record this automorphism rather than actually performing it if (isSetAutomorphVals2()) { // defined in NumbTh.h recordAutomorphVal2(amt); return; } //cerr << "********* automorph " << amt << "\n"; automorph(amt); reLinearize(keyID); k = MulMod(k, InvMod(amt,m), m); } FHE_TIMER_STOP; }
// Assumes current zz_p modulus is p^r // computes S = F^{-1} mod G via Hensel lifting void InvModpr(zz_pX& S, const zz_pX& F, const zz_pX& G, long p, long r) { ZZX ff, gg, ss, tt; ff = to_ZZX(F); gg = to_ZZX(G); zz_pBak bak; bak.save(); zz_p::init(p); zz_pX f, g, s, t; f = to_zz_pX(ff); g = to_zz_pX(gg); s = InvMod(f, g); t = (1-s*f)/g; assert(s*f + t*g == 1); ss = to_ZZX(s); tt = to_ZZX(t); ZZ pk = to_ZZ(1); for (long k = 1; k < r; k++) { // lift from p^k to p^{k+1} pk = pk * p; assert(divide(ss*ff + tt*gg - 1, pk)); zz_pX d = to_zz_pX( (1 - (ss*ff + tt*gg))/pk ); zz_pX s1, t1; s1 = (s * d) % g; t1 = (d-s1*f)/g; ss = ss + pk*to_ZZX(s1); tt = tt + pk*to_ZZX(t1); } bak.restore(); S = to_zz_pX(ss); assert((S*F) % G == 1); }
ZZ PaillierParty::decrypt(const std::map<uint32_t,ZZ> &partialCiphers, const std::vector<ZZ> &pubKeys) { ZZ sum(0); for (auto &pubKey : pubKeys) { sum = AddMod(sum,pubKey,m_n); } ZZ phi = MulMod(m_a,sum,m_n); ZZ prod(1); for (auto &partialCipher: partialCiphers) { uint32_t partyId = partialCipher.first; prod = MulMod(prod,PowerMod(partialCipher.second, 2*m_lagrangeBasis[partyId],m_field),m_field); } ZZ LResult = L_function(prod); ZZ combinedShare = InvMod(MulMod(4*m_delta*m_delta,phi,m_n),m_n); return MulMod(LResult, combinedShare, m_n); }
static void init_representatives(Vec<long>& representatives, long dim, const Vec<long>& mvec, const PAlgebra& zMStar) { assert(dim >= 0 && dim < mvec.length()); // special case if (dim >= LONG(zMStar.numOfGens())) { representatives.SetLength(1); representatives[0] = 1; return; } long m = mvec[dim]; long D = zMStar.OrderOf(dim); long g = InvMod(zMStar.ZmStarGen(dim) % m, m); representatives.SetLength(D); for (long i = 0; i < D; i++) representatives[i] = PowerMod(g, i, m); }
static void init_representatives(Vec<long>& representatives, long dim, const Vec<long>& mvec, const PAlgebra& zMStar) { //OLD: assert(dim >= 0 && dim < mvec.length()); helib::assertInRange(dim, 0l, mvec.length(), "Invalid argument: dim must be between 0 and mvec.length()"); // special case if (dim >= LONG(zMStar.numOfGens())) { representatives.SetLength(1); representatives[0] = 1; return; } long m = mvec[dim]; long D = zMStar.OrderOf(dim); long g = InvMod(zMStar.ZmStarGen(dim) % m, m); representatives.SetLength(D); for (long i = 0; i < D; i++) representatives[i] = PowerMod(g, i, m); }
ZZ get_PowerMod(const ZZ& numar, const ZZ& exponent, const ZZ& baza) { if (exponent == 0) return to_ZZ(1); long k = NumBits(exponent); ZZ res; res = 1; for (long i = k-1; i >= 0; i--) { res = (res*res) % baza; if (bit(exponent, i) == 1) res = (res*numar) % n; } if (exponent < 0) return InvMod(res, baza); else return res; }
static void compute_a_vals(Vec<ZZ>& a, long p, long e) // computes a[m] = a(m)/m! for m = p..(e-1)(p-1)+1, // as defined by Chen and Han. // a.length() is set to (e-1)(p-1)+2 { ZZ p_to_e = power_ZZ(p, e); ZZ p_to_2e = power_ZZ(p, 2*e); long len = (e-1)*(p-1)+2; ZZ_pPush push(p_to_2e); ZZ_pX x_plus_1_to_p = power(ZZ_pX(INIT_MONO, 1) + 1, p); ZZ_pX denom = InvTrunc(x_plus_1_to_p - ZZ_pX(INIT_MONO, p), len); ZZ_pX poly = MulTrunc(x_plus_1_to_p, denom, len); poly *= p; a.SetLength(len); ZZ m_fac(1); for (long m = 2; m < p; m++) { m_fac = MulMod(m_fac, m, p_to_2e); } for (long m = p; m < len; m++) { m_fac = MulMod(m_fac, m, p_to_2e); ZZ c = rep(coeff(poly, m)); ZZ d = GCD(m_fac, p_to_2e); if (d == 0 || d > p_to_e || c % d != 0) Error("cannot divide"); ZZ m_fac_deflated = (m_fac / d) % p_to_e; ZZ c_deflated = (c / d) % p_to_e; a[m] = MulMod(c_deflated, InvMod(m_fac_deflated, p_to_e), p_to_e); } }
void make_rsa_key(rsa_pub &pub, rsa_priv &priv, long bits, ZZ& e) { pub.e = e; do { GenPrime(priv.p, bits/2); } while (!IsOne(GCD(priv.p-1, pub.e))); do { GenPrime(priv.q, bits/2); } while (!IsOne(GCD(priv.q-1, pub.e))); pub.N = priv.p * priv.q; priv.d = InvMod(pub.e, (priv.p-1)*(priv.q-1)); rem(priv.dp1, priv.d, priv.p-1); rem(priv.dq1, priv.d, priv.q-1); }
long gauss(mat_zz_pE& M_in, long w) { long k, l; long i, j; long pos; zz_pX t1, t2, t3; zz_pX *x, *y; long n = M_in.NumRows(); long m = M_in.NumCols(); if (w < 0 || w > m) Error("gauss: bad args"); const zz_pXModulus& p = zz_pE::modulus(); vec_zz_pX *M = newNTL_NEW_OP vec_zz_pX[n]; for (i = 0; i < n; i++) { M[i].SetLength(m); for (j = 0; j < m; j++) { M[i][j].rep.SetMaxLength(2*deg(p)-1); M[i][j] = rep(M_in[i][j]); } } l = 0; for (k = 0; k < w && l < n; k++) { pos = -1; for (i = l; i < n; i++) { rem(t1, M[i][k], p); M[i][k] = t1; if (pos == -1 && !IsZero(t1)) { pos = i; } } if (pos != -1) { swap(M[pos], M[l]); InvMod(t3, M[l][k], p); negate(t3, t3); for (j = k+1; j < m; j++) { rem(M[l][j], M[l][j], p); } for (i = l+1; i < n; i++) { // M[i] = M[i] + M[l]*M[i,k]*t3 MulMod(t1, M[i][k], t3, p); clear(M[i][k]); x = M[i].elts() + (k+1); y = M[l].elts() + (k+1); for (j = k+1; j < m; j++, x++, y++) { // *x = *x + (*y)*t1 mul(t2, *y, t1); add(t2, t2, *x); *x = t2; } } l++; } } for (i = 0; i < n; i++) for (j = 0; j < m; j++) conv(M_in[i][j], M[i][j]); delete [] M; return l; }
void* worker(void* arg) { State& state = *((State*) arg); long k = state.k; #ifdef USE_THREADS pthread_mutex_lock(&state.lock); #endif while (1) { if (state.next * BLOCK_SIZE < state.bound) { // need to generate more modular data long next = state.next++; #ifdef USE_THREADS pthread_mutex_unlock(&state.lock); #endif Item* item = new Item; mpz_set_ui(item->modulus, 1); mpz_set_ui(item->residue, 0); for (long p = max(5, state.table->next_prime(next * BLOCK_SIZE)); p < state.bound && p < (next+1) * BLOCK_SIZE; p = state.table->next_prime(p)) { if (k % (p-1) == 0) continue; // compute B_k mod p long b = bern_modp(p, k); // CRT into running total long x = MulMod(SubMod(b, mpz_fdiv_ui(item->residue, p), p), InvMod(mpz_fdiv_ui(item->modulus, p), p), p); mpz_addmul_ui(item->residue, item->modulus, x); mpz_mul_ui(item->modulus, item->modulus, p); } #ifdef USE_THREADS pthread_mutex_lock(&state.lock); #endif state.items.insert(item); } else { // all modular data has been generated if (state.items.size() <= 1) { // no more CRTs for this thread to perform #ifdef USE_THREADS pthread_mutex_unlock(&state.lock); #endif return NULL; } // CRT two smallest items together Item* item1 = *(state.items.begin()); state.items.erase(state.items.begin()); Item* item2 = *(state.items.begin()); state.items.erase(state.items.begin()); #ifdef USE_THREADS pthread_mutex_unlock(&state.lock); #endif Item* item3 = CRT(item1, item2); delete item1; delete item2; #ifdef USE_THREADS pthread_mutex_lock(&state.lock); #endif state.items.insert(item3); } } }
newNTL_START_IMPL zz_pInfoT::zz_pInfoT(long NewP, long maxroot) { ref_count = 1; if (maxroot < 0) Error("zz_pContext: maxroot may not be negative"); if (NewP <= 1) Error("zz_pContext: p must be > 1"); if (NumBits(NewP) > newNTL_SP_NBITS) Error("zz_pContext: modulus too big"); ZZ P, B, M, M1, MinusM; long n, i; long q, t; p = NewP; pinv = 1/double(p); index = -1; conv(P, p); sqr(B, P); LeftShift(B, B, maxroot+newNTL_FFTFudge); set(M); n = 0; while (M <= B) { UseFFTPrime(n); q = FFTPrime[n]; n++; mul(M, M, q); } if (n > 4) Error("zz_pInit: too many primes"); NumPrimes = n; PrimeCnt = n; MaxRoot = CalcMaxRoot(q); if (maxroot < MaxRoot) MaxRoot = maxroot; negate(MinusM, M); MinusMModP = rem(MinusM, p); if (!(CoeffModP = (long *) newNTL_MALLOC(n, sizeof(long), 0))) Error("out of space"); if (!(x = (double *) newNTL_MALLOC(n, sizeof(double), 0))) Error("out of space"); if (!(u = (long *) newNTL_MALLOC(n, sizeof(long), 0))) Error("out of space"); for (i = 0; i < n; i++) { q = FFTPrime[i]; div(M1, M, q); t = rem(M1, q); t = InvMod(t, q); mul(M1, M1, t); CoeffModP[i] = rem(M1, p); x[i] = ((double) t)/((double) q); u[i] = t; } }
YASHE::YASHE(long pModulus_, long log2CModulus, long cyclotomicDegree, long stdDev_, long log2Radix) { // Check if already written { std::string name = getFileName(pModulus_, log2CModulus, cyclotomicDegree, stdDev_, log2Radix); std::ifstream f(name.c_str()); if (f.good()) { *this = readFromFile(name); return; } } pModulus = pModulus_; cModulus = NTL::GenPrime_ZZ(log2CModulus); bigPModulus = NTL::ZZ(pModulus); NTL::ZZ_p::init(cModulus); modulusRatio = NTL::conv<NTL::ZZ_p>(cModulus/pModulus); bigModulus = (cModulus * cModulus)/pModulus; if (NumberTheory::eulerToitient(cyclotomicDegree) < cModulus) { maxDegree = NumberTheory::eulerToitient(cyclotomicDegree) - 1; cycloModX = NumberTheory::cyclotomicPoly(cyclotomicDegree); } else { long smallCModulus = rem(cModulus, LONG_MAX); maxDegree = smallCModulus - 1; cycloModX = NTL::ZZX(NTL::INIT_MONO, smallCModulus) - 1; } cycloMod = NTL::ZZ_pXModulus(NTL::conv<NTL::ZZ_pX>(cycloModX)); { NTL::ZZ_pPush push(bigPModulus); // switch to plain text modulus // Factor the cyclotomic polynomial modulo t // for batch encryption NTL::ZZ_pXModulus pModulusX; NTL::build(pModulusX, NTL::conv<NTL::ZZ_pX>(cycloModX)); NTL::vec_ZZ_pX factors_; NTL::SFCanZass(factors_, pModulusX); factors.resize(factors_.length()); for (long i = 0; i < factors_.length(); i++) { factors[i] = factors_[i]; } crtElements.resize(factors.size()); NTL::ZZ_pX fInv, fInvInv; for (long i = 0; i < factors.size(); i++) { div(fInv, NTL::conv<NTL::ZZ_pX>(cycloModX), factors[i]); rem(fInvInv, fInv, factors[i]); InvMod(fInvInv, fInvInv, factors[i]); crtElements[i] = MulMod(fInv, fInvInv, pModulusX); } } { NTL::ZZ_pPush push(bigModulus); // switch to multiplication modulus // make another modulus for fast multiplication bigCycloMod = NTL::ZZ_pXModulus(NTL::conv<NTL::ZZ_pX>(cycloModX)); } stdDev = stdDev_; radix = NTL::power2_ZZ(log2Radix); decompSize = log(cModulus)/log(radix) + 1; // log_w(q) + 1 randGen = std::mt19937(time(0)); writeToFile(getFileName(pModulus, log2CModulus, cyclotomicDegree, stdDev, log2Radix)); }
void ZZ_p::DoInstall() { SmartPtr<ZZ_pTmpSpaceT> tmps = 0; do { // NOTE: thread safe lazy init Lazy<ZZ_pFFTInfoT>::Builder builder(ZZ_pInfo->FFTInfo); if (!builder()) break; UniquePtr<ZZ_pFFTInfoT> FFTInfo; FFTInfo.make(); ZZ B, M, M1, M2, M3; long n, i; long q, t; mulmod_t qinv; sqr(B, ZZ_pInfo->p); LeftShift(B, B, NTL_FFTMaxRoot+NTL_FFTFudge); // FIXME: the following is quadratic time...would // be nice to get a faster solution... // One could estimate the # of primes by summing logs, // then multiply using a tree-based multiply, then // adjust up or down... // Assuming IEEE floating point, the worst case estimate // for error guarantees a correct answer +/- 1 for // numprimes up to 2^25...for sure we won't be // using that many primes...we can certainly put in // a sanity check, though. // If I want a more accuaruate summation (with using Kahan, // which has some portability issues), I could represent // numbers as x = a + f, where a is integer and f is the fractional // part. Summing in this representation introduces an *absolute* // error of 2 epsilon n, which is just as good as Kahan // for this application. // same strategy could also be used in the ZZX HomMul routine, // if we ever want to make that subquadratic set(M); n = 0; while (M <= B) { UseFFTPrime(n); q = GetFFTPrime(n); n++; mul(M, M, q); } FFTInfo->NumPrimes = n; FFTInfo->MaxRoot = CalcMaxRoot(q); double fn = double(n); if (8.0*fn*(fn+48) > NTL_FDOUBLE_PRECISION) ResourceError("modulus too big"); if (8.0*fn*(fn+48) <= NTL_FDOUBLE_PRECISION/double(NTL_SP_BOUND)) FFTInfo->QuickCRT = true; else FFTInfo->QuickCRT = false; // FIXME: some of this stuff does not need to be initialized // at all if FFTInfo->crt_struct.special() FFTInfo->x.SetLength(n); FFTInfo->u.SetLength(n); FFTInfo->uqinv.SetLength(n); FFTInfo->rem_struct.init(n, ZZ_pInfo->p, GetFFTPrime); FFTInfo->crt_struct.init(n, ZZ_pInfo->p, GetFFTPrime); if (!FFTInfo->crt_struct.special()) { ZZ qq, rr; DivRem(qq, rr, M, ZZ_pInfo->p); NegateMod(FFTInfo->MinusMModP, rr, ZZ_pInfo->p); for (i = 0; i < n; i++) { q = GetFFTPrime(i); qinv = GetFFTPrimeInv(i); long tt = rem(qq, q); mul(M2, ZZ_pInfo->p, tt); add(M2, M2, rr); div(M2, M2, q); // = (M/q) rem p div(M1, M, q); t = rem(M1, q); t = InvMod(t, q); mul(M3, M2, t); rem(M3, M3, ZZ_pInfo->p); FFTInfo->crt_struct.insert(i, M3); FFTInfo->x[i] = ((double) t)/((double) q); FFTInfo->u[i] = t; FFTInfo->uqinv[i] = PrepMulModPrecon(FFTInfo->u[i], q, qinv); } } tmps = MakeSmart<ZZ_pTmpSpaceT>(); tmps->crt_tmp_vec.fetch(FFTInfo->crt_struct); tmps->rem_tmp_vec.fetch(FFTInfo->rem_struct); builder.move(FFTInfo); } while (0); if (!tmps) { const ZZ_pFFTInfoT *FFTInfo = ZZ_pInfo->FFTInfo.get(); tmps = MakeSmart<ZZ_pTmpSpaceT>(); tmps->crt_tmp_vec.fetch(FFTInfo->crt_struct); tmps->rem_tmp_vec.fetch(FFTInfo->rem_struct); } ZZ_pTmpSpace = tmps; }
// Constructor: it is assumed that zms is already set with m>1 // If q == 0, then the current context is used Cmodulus::Cmodulus(const PAlgebra &zms, long qq, long rt) { assert(zms.getM()>1); bool explicitModulus = true; if (qq == 0) { q = zz_p::modulus(); explicitModulus = false; } else q = qq; zMStar = &zms; root = rt; long mm; mm = zms.getM(); m_inv = InvMod(mm, q); zz_pBak bak; if (zms.getPow2()) { // special case when m is a power of 2 assert( explicitModulus ); bak.save(); RandomState state; SetSeed(conv<ZZ>("84547180875373941534287406458029")); // DIRT: this ensures the roots are deterministically generated // inside the zz_pContext constructor context = zz_pContext(INIT_USER_FFT, q); state.restore(); context.restore(); powers.set_ptr(new zz_pX); ipowers.set_ptr(new zz_pX); long k = zms.getPow2(); long phim = 1L << (k-1); assert(k <= zz_pInfo->MaxRoot); // rootTables get initialized 0..zz_pInfo->Maxroot #ifdef FHE_OPENCL altFFTInfo = MakeSmart<AltFFTPrimeInfo>(); InitAltFFTPrimeInfo(*altFFTInfo, *zz_pInfo->p_info, k-1); #endif long w0 = zz_pInfo->p_info->RootTable[0][k]; long w1 = zz_pInfo->p_info->RootTable[1][k]; powers->rep.SetLength(phim); powers_aux.SetLength(phim); for (long i = 0, w = 1; i < phim; i++) { powers->rep[i] = w; powers_aux[i] = PrepMulModPrecon(w, q); w = MulMod(w, w0, q); } ipowers->rep.SetLength(phim); ipowers_aux.SetLength(phim); for (long i = 0, w = 1; i < phim; i++) { ipowers->rep[i] = w; ipowers_aux[i] = PrepMulModPrecon(w, q); w = MulMod(w, w1, q); } return; } if (explicitModulus) { bak.save(); // backup the current modulus context = BuildContext(q, NextPowerOfTwo(zms.getM()) + 1); context.restore(); // set NTL's current modulus to q } else context.save(); if (root==0) { // Find a 2m-th root of unity modulo q, if not given zz_p rtp; long e = 2*zms.getM(); FindPrimitiveRoot(rtp,e); // NTL routine, relative to current modulus if (rtp==0) // sanity check Error("Cmod::compRoots(): no 2m'th roots of unity mod q"); root = rep(rtp); } rInv = InvMod(root,q); // set rInv = root^{-1} mod q // Allocate memory (relative to current modulus that was defined above). // These objects will be initialized when anyone calls FFT/iFFT. zz_pX phimx_poly; conv(phimx_poly, zms.getPhimX()); powers.set_ptr(new zz_pX); Rb.set_ptr(new fftRep); ipowers.set_ptr(new zz_pX); iRb.set_ptr(new fftRep); phimx.set_ptr(new zz_pXModulus1(zms.getM(), phimx_poly)); BluesteinInit(mm, conv<zz_p>(root), *powers, powers_aux, *Rb); BluesteinInit(mm, conv<zz_p>(rInv), *ipowers, ipowers_aux, *iRb); }
long CRT(mat_ZZ& gg, ZZ& a, const mat_zz_p& G) { long n = gg.NumRows(); long m = gg.NumCols(); if (G.NumRows() != n || G.NumCols() != m) Error("CRT: dimension mismatch"); long p = zz_p::modulus(); ZZ new_a; mul(new_a, a, p); long a_inv; a_inv = rem(a, p); a_inv = InvMod(a_inv, p); long p1; p1 = p >> 1; ZZ a1; RightShift(a1, a, 1); long p_odd = (p & 1); long modified = 0; long h; ZZ g; long i, j; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { if (!CRTInRange(gg[i][j], a)) { modified = 1; rem(g, gg[i][j], a); if (g > a1) sub(g, g, a); } else g = gg[i][j]; h = rem(g, p); h = SubMod(rep(G[i][j]), h, p); h = MulMod(h, a_inv, p); if (h > p1) h = h - p; if (h != 0) { modified = 1; if (!p_odd && g > 0 && (h == p1)) MulSubFrom(g, a, h); else MulAddTo(g, a, h); } gg[i][j] = g; } } a = new_a; return modified; }
void determinant(zz_pE& d, const mat_zz_pE& M_in) { long k, n; long i, j; long pos; zz_pX t1, t2; zz_pX *x, *y; const zz_pXModulus& p = zz_pE::modulus(); n = M_in.NumRows(); if (M_in.NumCols() != n) Error("determinant: nonsquare matrix"); if (n == 0) { set(d); return; } vec_zz_pX *M = newNTL_NEW_OP vec_zz_pX[n]; for (i = 0; i < n; i++) { M[i].SetLength(n); for (j = 0; j < n; j++) { M[i][j].rep.SetMaxLength(2*deg(p)-1); M[i][j] = rep(M_in[i][j]); } } zz_pX det; set(det); for (k = 0; k < n; k++) { pos = -1; for (i = k; i < n; i++) { rem(t1, M[i][k], p); M[i][k] = t1; if (pos == -1 && !IsZero(t1)) pos = i; } if (pos != -1) { if (k != pos) { swap(M[pos], M[k]); negate(det, det); } MulMod(det, det, M[k][k], p); // make M[k, k] == -1 mod p, and make row k reduced InvMod(t1, M[k][k], p); negate(t1, t1); for (j = k+1; j < n; j++) { rem(t2, M[k][j], p); MulMod(M[k][j], t2, t1, p); } for (i = k+1; i < n; i++) { // M[i] = M[i] + M[k]*M[i,k] t1 = M[i][k]; // this is already reduced x = M[i].elts() + (k+1); y = M[k].elts() + (k+1); for (j = k+1; j < n; j++, x++, y++) { // *x = *x + (*y)*t1 mul(t2, *y, t1); add(*x, *x, t2); } } } else { clear(d); goto done; } } conv(d, det); done: delete[] M; }
void inv(zz_pE& d, mat_zz_pE& X, const mat_zz_pE& A) { long n = A.NumRows(); if (A.NumCols() != n) Error("inv: nonsquare matrix"); if (n == 0) { set(d); X.SetDims(0, 0); return; } long i, j, k, pos; zz_pX t1, t2; zz_pX *x, *y; const zz_pXModulus& p = zz_pE::modulus(); vec_zz_pX *M = newNTL_NEW_OP vec_zz_pX[n]; for (i = 0; i < n; i++) { M[i].SetLength(2*n); for (j = 0; j < n; j++) { M[i][j].rep.SetMaxLength(2*deg(p)-1); M[i][j] = rep(A[i][j]); M[i][n+j].rep.SetMaxLength(2*deg(p)-1); clear(M[i][n+j]); } set(M[i][n+i]); } zz_pX det; set(det); for (k = 0; k < n; k++) { pos = -1; for (i = k; i < n; i++) { rem(t1, M[i][k], p); M[i][k] = t1; if (pos == -1 && !IsZero(t1)) { pos = i; } } if (pos != -1) { if (k != pos) { swap(M[pos], M[k]); negate(det, det); } MulMod(det, det, M[k][k], p); // make M[k, k] == -1 mod p, and make row k reduced InvMod(t1, M[k][k], p); negate(t1, t1); for (j = k+1; j < 2*n; j++) { rem(t2, M[k][j], p); MulMod(M[k][j], t2, t1, p); } for (i = k+1; i < n; i++) { // M[i] = M[i] + M[k]*M[i,k] t1 = M[i][k]; // this is already reduced x = M[i].elts() + (k+1); y = M[k].elts() + (k+1); for (j = k+1; j < 2*n; j++, x++, y++) { // *x = *x + (*y)*t1 mul(t2, *y, t1); add(*x, *x, t2); } } } else { clear(d); goto done; } } X.SetDims(n, n); for (k = 0; k < n; k++) { for (i = n-1; i >= 0; i--) { clear(t1); for (j = i+1; j < n; j++) { mul(t2, rep(X[j][k]), M[i][j]); add(t1, t1, t2); } sub(t1, t1, M[i][n+k]); conv(X[i][k], t1); } } conv(d, det); done: delete[] M; }