/** * Method based on FFT to find the determinant and the first coefficient of w(x) this suffices to * find w(x) completely * * @param q the polynomial * @param n the degree * @return The determinant and the first coefficient of w(x). */ static BigInteger[] gzModZ2(Polynomial q, int n) { int i; int N = 1 << n; Polynomial V = new Polynomial(q.coeffs); // V = q Polynomial U = new Polynomial(1); // U = 1 U.setCoeff(0, new BigInteger("1")); Polynomial F = new Polynomial(N); // F(x) = x^N +1 F.setCoeff(0, new BigInteger("1")); F.setCoeff(N, new BigInteger("1")); Polynomial V2 = new Polynomial(N); while (N > 1) { V2 = new Polynomial(V.coeffs); for (i = 1; i <= V2.degree; i += 2) { // set V2(x) := V(-x) V2.coeffs[i] = V2.coeffs[i].negate(); // negate odd coefficients } V = Polynomial.mod(Polynomial.mult(V, V2), F); // V := V(x) * V(-x) mod f(x) U = Polynomial.mod(Polynomial.mult(U, V2), F); // U := U(x) * V(-x) mod f(x) // Sanity-check: verify that the odd coefficients in V are zero for (i = 1; i <= V.degree; i += 2) if (!V.coeffs[i].equals(new BigInteger("0"))) { return null; } // "Compress" the non-zero coefficients of V for (i = 1; i <= V.degree / 2; i++) V.coeffs[i] = V.coeffs[2 * i]; for (; i <= V.degree; i++) V.coeffs[i] = new BigInteger("0"); V.normalize(); // Set U to the "compressed" ( U(x) + U(-x) ) /2 for (i = 0; i <= U.degree / 2; i++) U.coeffs[i] = U.coeffs[2 * i]; for (; i <= U.degree; i++) U.coeffs[i] = new BigInteger("0"); U.normalize(); // Set N := N/2 and update F accordingly F.coeffs[N] = new BigInteger("0"); N >>= 1; F.coeffs[N] = new BigInteger("1"); F.normalize(); } return new BigInteger[] {V.coeffs[0], U.coeffs[0]}; }
/** * Verifies whether the polynomial q yields a lattice with the correct HNF and odd resultant, * computes the determinant (or resultant), root and inverse polynomial w(x) * * @param q the polynomial yielding the lattice * @param n the dimension * @return True if the polynomial suffices, false otherwise. */ boolean invModFx(Polynomial q, int n) { int N = 1 << n; BigInteger res, wi = null; if (q.degree >= N) return false; // compute resultant and w0 BigInteger w0, w1; BigInteger rw[] = gzModZ2(q, n); res = rw[0]; w0 = rw[1]; if (Functions.isEven(res)) { // Resultant must be odd return false; } // repeat for the polynomial x*q(x) mod x^N+1 Polynomial qx = new Polynomial(N); for (int i = N - 1; i > 0; i--) { qx.setCoeff(i, q.coeffs[i - 1]); // copy 1st N-1 coeffs } qx.setCoeff(0, q.coeffs[N - 1].negate()); // negate last coeff qx.normalize(); rw = gzModZ2(qx, n); res = rw[0]; w1 = rw[1]; // now that we have res, w0, w1, set root = w1/w0 mod res // make sure things are positive if (res.signum() == -1) { res = res.negate(); w0 = w0.negate(); w1 = w1.negate(); } if (w0.signum() < 0) w0 = w0.add(res); if (w1.signum() < 0) w1 = w1.add(res); BigInteger inv = Functions.xgcd(w0, res); // returns a = w0^{-1} if w0 invertible, null otherwise if (inv == null) { return false; // verify that w0^{-1} exists } root = w1.multiply(inv).divideAndRemainder(res)[1]; // root= w1 * w0^{-1} mod res BigInteger tmp = root.modPow(new BigInteger(Integer.toString(N)), res); // it should hold that root^n = -1 mod res if (!tmp.add(new BigInteger("1")).equals(res)) { return false; } W = new BigInteger[N]; // get the entire polynomial w(x), for debug purposes W[0] = w0; W[1] = w1; for (int k = 2; k < N; k++) { W[k] = W[k - 1].multiply(root).mod(res); } // for decryption only a single odd coefficient of w(x) is necessary int i = 0; if (((w0.compareTo(res.shiftRight(1)) <= 0) && Functions.isOdd(w0)) || ((w0.compareTo(res.shiftRight(1)) > 0) && Functions.isEven(w0))) { wi = w0; } else { if (((w1.compareTo(res.shiftRight(1)) <= 0) && Functions.isOdd(w1)) || ((w1.compareTo(res.shiftRight(1)) > 0) && Functions.isEven(w1))) { wi = w1; } else { for (i = 2; i < N; i++) { w1 = w1.multiply(root).mod(res); // (w1.multiply(root)).divideAndRemainder(res)[1]; if (((w1.compareTo(res.shiftRight(1)) <= 0) && Functions.isOdd(w1)) || ((w1.compareTo(res.shiftRight(1)) > 0) && Functions.isEven(w1))) { wi = w1; break; } } } } det = res; w = wi; return ((i == N) ? false : true); // We get i==N only if all the wi's are even }