def add(self, P, Q): """ Elliptic Curve Addition Args: P: Point on curve Q: Point on curve Returns: R = P + Q """ if P == 0: return Q if Q == 0: return P if P[0] == Q[0] and P[1] == -Q[1] % self.p: return 0 if P == Q: l = (3 * P[0]**2 + self.a) * inverse(2*P[1], self.p) % self.p else: l = (P[1] - Q[1]) * inverse(P[0] - Q[0], self.p) % self.p x = (l**2 - P[0] - Q[0]) % self.p y = (l * (P[0] - x) - P[1]) % self.p return x, y
def add(self, P, Q): """ Elliptic Curve Addition Args: P: Point on curve Q: Point on curve Returns: R = P + Q """ if P == 0: return Q if Q == 0: return P if P[0] == Q[0] and P[1] == -Q[1] % self.p: return 0 if P == Q: l = (3 * P[0]**2 + self.a) * inverse(2 * P[1], self.p) % self.p else: l = (P[1] - Q[1]) * inverse(P[0] - Q[0], self.p) % self.p x = (l**2 - P[0] - Q[0]) % self.p y = (l * (P[0] - x) - P[1]) % self.p return x, y
def make_poly(self): """ Make coefficients of f(x)= ax^2+b*x+c """ T = time.time() if self.d_list == []: d = int(math.sqrt((math.sqrt(self.number)/(math.sqrt(2)*self.Srange)))) if d%2 == 0: if (d+1)%4 == 1: #case d=0 mod4 d += 3 else: d += 1 #case d=2 mod4 elif d%4 == 1: #case d=1 mod4 d += 2 #case d=3 mod4 else: d = self.d_list[-1] while d in self.d_list or not prime.primeq(d) or \ arith1.legendre(self.number,d) != 1 or d in self.FB: d += 4 a = d**2 h_0 = pow(self.number, (d-3)//4, d) h_1 = (h_0*self.number) % d h_2 = ((arith1.inverse(2,d)*h_0*(self.number - h_1**2))/d) % d b = (h_1 + h_2*d) % a if b%2 == 0: b = b - a self.d_list.append(d) self.a_list.append(a) self.b_list.append(b) # Get solution of F(x) = 0 (mod p^i) solution = [] i = 0 for s in self.Nsqrt: k = 0 p_solution = [] ppow = 1 while k < len(s): ppow *= self.FB[i+2] a_inverse = arith1.inverse(2*self.a_list[-1], ppow) x_1 = ((-b + s[k][0])*a_inverse) % ppow x_2 = ((-b + s[k][1])*a_inverse) % ppow p_solution.append([x_1, x_2]) k += 1 i += 1 solution.append(p_solution) self.solution = solution self.coefficienttime += time.time() - T
def _extgcdp(f, g, p): """ _extgcdp(f,g,p) -> u,v,w Find u,v,w such that f*u + g*v = w = gcd(f,g) mod p. p should be a prime number. This is a private function. """ modp = lambda c: c % p u, v, w, x, y, z = the_one, the_zero, f, the_zero, the_one, g while z: zlc = z.leading_coefficient() if zlc != 1: normalizer = arith1.inverse(zlc, p) x = (x * normalizer).coefficients_map(modp) y = (y * normalizer).coefficients_map(modp) z = (z * normalizer).coefficients_map(modp) q = w.pseudo_floordiv(z) u, v, w, x, y, z = x, y, z, u - q*x, v - q*y, w - q*z x = x.coefficients_map(modp) y = y.coefficients_map(modp) z = z.coefficients_map(modp) if w.degree() == 0 and w[0] != 1: u = u.scalar_exact_division(w[0]) # u / w v = v.scalar_exact_division(w[0]) # v / w w = w.getRing().one # w / w return u, v, w
def _extgcdp(f, g, p): """ _extgcdp(f,g,p) -> u,v,w Find u,v,w such that f*u + g*v = w = gcd(f,g) mod p. p should be a prime number. This is a private function. """ modp = lambda c: c % p u, v, w, x, y, z = the_one, the_zero, f, the_zero, the_one, g while z: zlc = z.leading_coefficient() if zlc != 1: normalizer = arith1.inverse(zlc, p) x = (x * normalizer).coefficients_map(modp) y = (y * normalizer).coefficients_map(modp) z = (z * normalizer).coefficients_map(modp) q = w.pseudo_floordiv(z) u, v, w, x, y, z = x, y, z, u - q * x, v - q * y, w - q * z x = x.coefficients_map(modp) y = y.coefficients_map(modp) z = z.coefficients_map(modp) if w.degree() == 0 and w[0] != 1: u = u.scalar_exact_division(w[0]) # u / w v = v.scalar_exact_division(w[0]) # v / w w = w.getRing().one # w / w return u, v, w
def _lucas_test_sequence(n, a, b): """ Return x_0, x_1, x_m, x_{m+1} of Lucas sequence of parameter a, b, where m = (n - (a**2 - 4*b / n)) // 2. """ d = a**2 - 4 * b if (d >= 0 and arith1.issquare(d) or not (gcd.coprime(n, 2 * a * b * d))): raise ValueError("Choose another parameters.") x_0 = 2 inv_b = arith1.inverse(b, n) x_1 = ((a**2) * inv_b - 2) % n # Chain functions def even_step(u): """ 'double' u. """ return (u**2 - x_0) % n def odd_step(u, v): """ 'add' u and v. """ return (u * v - x_1) % n m = (n - arith1.legendre(d, n)) // 2 x_m, x_mplus1 = _lucas_chain(m, even_step, odd_step, x_0, x_1) return x_0, x_1, x_m, x_mplus1
def _lucas_test_sequence(n, a, b): """ Return x_0, x_1, x_m, x_{m+1} of Lucas sequence of parameter a, b, where m = (n - (a**2 - 4*b / n)) // 2. """ d = a**2 - 4*b if (d >= 0 and arith1.floorsqrt(d) ** 2 == d) \ or not(gcd.coprime(n, 2*a*b*d)): raise ValueError("Choose another parameters.") x_0 = 2 inv_b = arith1.inverse(b, n) x_1 = ((a ** 2) * inv_b - 2) % n # Chain functions def even_step(u): """ 'double' u. """ return (u**2 - x_0) % n def odd_step(u, v): """ 'add' u and v. """ return (u*v - x_1) % n m = (n - arith1.legendre(d, n)) // 2 x_m, x_mplus1 = Lucas_chain(m, even_step, odd_step, x_0, x_1) return x_0, x_1, x_m, x_mplus1
def get_random_curve_with_point(cls, curve_type, n, bounds): """ Return the curve with parameter C and a point Q on the curve, according to the curve_type, factorization target n and the bounds for stages. curve_type should be one of the module constants corresponding to parameters: S: Suyama's parameter selection strategy B: Bernstein's [2:1], [16,18,4,2] A1: Asuncion's [2:1], [4,14,1,1] A2: Asuncion's [2:1], [16,174,4,41] A3: Asuncion's [3:1], [9,48,1,2] A4: Asuncion's [3:1], [9,39,1,1] A5: Asuncion's [4:1], [16,84,1,1] N3: Nakamura's [2:1], [28,22,7,3] This is a class method. """ bound = bounds.first if curve_type not in cls._CURVE_TYPES: raise ValueError("Input curve_type is wrong.") if curve_type == SUYAMA: t = n while _gcd.gcd(t, n) != 1: sigma = random.randrange(6, bound + 1) u, v = (sigma**2 - 5) % n, (4 * sigma) % n t = 4 * (u**3) * v d = _arith1.inverse(t, n) curve = cls((((u - v)**3 * (3 * u + v)) * d - 2) % n) start_point = Point(pow(u, 3, n), pow(v, 3, n)) elif curve_type == BERNSTEIN: d = random.randrange(1, bound + 1) start_point = Point(2, 1) curve = cls((4 * d + 2) % n) elif curve_type == ASUNCION1: d = random.randrange(1, bound + 1) start_point = Point(2, 1) curve = cls((d + 1) % n) elif curve_type == ASUNCION2: d = random.randrange(1, bound + 1) start_point = Point(2, 1) curve = cls((4 * d + 41) % n) elif curve_type == ASUNCION3: d = random.randrange(1, bound + 1) start_point = Point(3, 1) curve = cls((d + 2) % n) elif curve_type == ASUNCION4: d = random.randrange(1, bound + 1) start_point = Point(3, 1) curve = cls((d + 1) % n) elif curve_type == ASUNCION5: d = random.randrange(1, bound + 1) start_point = Point(4, 1) curve = cls((d + 1) % n) elif curve_type == NAKAMURA3: d = random.randrange(1, bound + 1) start_point = Point(2, 1) curve = cls((7 * d + 3) % n) return curve, start_point
def e1_ZnZ(x, n): """ Return the solution of x[0] + x[1]*t = 0 (mod n). x[0], x[1] and n must be positive integers. """ try: return (-x[0] * arith1.inverse(x[1], n)) % n except ZeroDivisionError: raise ValueError("No Solution")
def get_random_curve_with_point(cls, curve_type, n, bounds): """ Return the curve with parameter C and a point Q on the curve, according to the curve_type, factorization target n and the bounds for stages. curve_type should be one of the module constants corresponding to parameters: S: Suyama's parameter selection strategy B: Bernstein's [2:1], [16,18,4,2] A1: Asuncion's [2:1], [4,14,1,1] A2: Asuncion's [2:1], [16,174,4,41] A3: Asuncion's [3:1], [9,48,1,2] A4: Asuncion's [3:1], [9,39,1,1] A5: Asuncion's [4:1], [16,84,1,1] This is a class method. """ bound = bounds.first if curve_type not in cls._CURVE_TYPES: raise ValueError("Input curve_type is wrong.") if curve_type == SUYAMA: t = n while _gcd.gcd(t, n) != 1: sigma = random.randrange(6, bound + 1) u, v = (sigma**2 - 5) % n, (4*sigma) % n t = 4*(u**3)*v d = _arith1.inverse(t, n) curve = cls((((u - v)**3 * (3*u + v)) * d - 2) % n) start_point = Point(pow(u, 3, n), pow(v, 3, n)) elif curve_type == BERNSTEIN: d = random.randrange(1, bound + 1) start_point = Point(2, 1) curve = cls((4*d + 2) % n) elif curve_type == ASUNCION1: d = random.randrange(1, bound + 1) start_point = Point(2, 1) curve = cls((d + 1) % n) elif curve_type == ASUNCION2: d = random.randrange(1, bound + 1) start_point = Point(2, 1) curve = cls((4*d + 41) % n) elif curve_type == ASUNCION3: d = random.randrange(1, bound + 1) start_point = Point(3, 1) curve = cls((d + 2) % n) elif curve_type == ASUNCION4: d = random.randrange(1, bound + 1) start_point = Point(3, 1) curve = cls((d + 1) % n) elif curve_type == ASUNCION5: d = random.randrange(1, bound + 1) start_point = Point(4, 1) curve = cls((d + 1) % n) return curve, start_point
def _generate_params_for_general_disc(disc, n, g): """ Generate curve params for discriminant other than -3 or -4. """ jr = equation.root_Fp([c % n for c in hilbert(disc)[-1]], n) c = (jr * arith1.inverse(jr - 1728, n)) % n r = (-3 * c) % n s = (2 * c) % n yield (r, s) g2 = (g * g) % n yield ((r * g2) % n, (s * g2 * g) % n)
def sqroot_power(a, p, n): """ return squareroot of a mod p^k for k = 2,3,...,n """ r = arith1.modsqrt(a, p) x = (r, p-r) i = 2 answer = [x] ppower = p while i <= n: b_1 = (x[0]**2-a) // ppower b_2 = (x[1]**2-a) // ppower x_1 = -b_1 * arith1.inverse(2*x[0], p) x_2 = -b_2 * arith1.inverse(2*x[1], p) X_1 = x[0] + x_1*ppower % (p*ppower) X_2 = x[1] + x_2*ppower % (p*ppower) x = [X_1, X_2] answer.append(x) i += 1 ppower *= p return answer
def reverse_FFT(values, bound): """ Reverse Fast Fourier Tfransform. """ mod = (1 << (bound >> 1)) + 1 shuf_values = values[:] reverse_FFT_coeffficients = FFT(shuf_values, bound) inverse = arith1.inverse(bound, mod) reverse_FFT_coeffficients = [ min_abs_mod(inverse * i, mod) for i in reverse_FFT_coeffficients ] reverse_FFT_coeffficients1 = reverse_FFT_coeffficients[:1] reverse_FFT_coeffficients2 = reverse_FFT_coeffficients[1:] reverse_FFT_coeffficients2.reverse() reverse_FFT_coeffficients_total = reverse_FFT_coeffficients1 + reverse_FFT_coeffficients2 return reverse_FFT_coeffficients_total
def generate_curve(p, d): ''' Essentially Algorithm 7.5.9 Args: p: Returns: parameters a, b for the curve ''' # calculate quadratic nonresidue g = gen_QNR(p, d) # find discriminant new_d = gen_discriminant(0) uv = cornacchia_smith(p, new_d) while jacobi(new_d, p) != 1 or uv is None: new_d = gen_discriminant(new_d) uv = cornacchia_smith(p, new_d) u, v = uv # storing the result of cornacchia. u^2 + v^2 * |D| = 4*p # check for -d = 3 or 4 # Choose one possible output # Look at param_gen for comparison. answer = [] if new_d == -3: x = -1 for i in range(0, 6): answer.append((0, x)) x = (x * g) % p return answer if new_d == -4: x = -1 for i in range(0, 4): answer.append((x, 0)) x = (x * g) % p return answer # otherwise compute the hilbert polynomial _, t, _ = hilbert(new_d) s = [i % p for i in t] j = equation.root_Fp(s, p) # Find a root for s in Fp. Algorithm 2.3.10 c = j * inverse(j - 1728, p) % p r = -3 * c % p s = 2 * c % p return [(r, s), (r * g * g % p, s * (g**3) % p)]
def curve_parameters(d, p): ''' Modified Algorithm 7.5.9 for the use of ecpp Args: d: discriminant p: number for prime proving Returns: a list of (a, b) parameters for ''' g = gen_QNR(p, d) #g = nzmath.ecpp.quasi_primitive(p, d==-3) u, v = cornacchia_smith(p, d) # go without the check for result of cornacchia because it's done by previous methods. if jacobi(d, p) != 1: raise ValueError('jacobi(d, p) not equal to 1.') # check for -d = 3 or 4 # Choose one possible output # Look at param_gen for comparison. answer = [] if d == -3: x = -1 % p for i in range(0, 6): answer.append((0, x)) x = (x * g) % p return answer if d == -4: x = -1 % p for i in range(0, 4): answer.append((x, 0)) x = (x * g) % p return answer # otherwise compute the hilbert polynomial _, t, _ = hilbert(d) s = [int(i % p) for i in t] j = equation.root_Fp(s, p) # Find a root for s in Fp. Algorithm 2.3.10 c = j * inverse(j - 1728, p) % p r = -3 * c % p s = 2 * c % p return [(r, s), (r * g * g % p, s * (g**3) % p)]
def SilverPohligHellman(target, base, p): """ Silver-Pohlig-Hellman method of DLP for finite prime fields. x, the discrete log of target, can be determined for each prime power factor of p - 1 (passed through factored_order): x = \sum s_j p_i**j mod p_i**e (0 <= s_j < p_i) Lidl, Niederreiter, 'Intro. to finite fields and their appl.' (revised ed) pp.356 (1994) CUP. """ log_mod_factor = {} order = p - 1 factored_order = factor_misc.FactoredInteger(order) base_inverse = arith1.inverse(base, p) for p_i, e in factored_order: log_mod_factor[p_i] = 0 smallorder = order modtarget = target primitive_root_of_unity = pow(base, order // p_i, p) p_i_power = 1 for j in range(e): smallorder //= p_i targetpow = pow(modtarget, smallorder, p) if targetpow == 1: s_j = 0 else: root_of_unity = primitive_root_of_unity for k in range(1, p_i): if targetpow == root_of_unity: s_j = k break root_of_unity = root_of_unity * primitive_root_of_unity % p log_mod_factor[p_i] += s_j * p_i_power modtarget = modtarget * pow(base_inverse, s_j * p_i_power, p) % p p_i_power *= p_i if p_i < p_i_power: log_mod_factor[p_i_power] = log_mod_factor[p_i] del log_mod_factor[p_i] if len(log_mod_factor) == 1: return list(log_mod_factor.values())[0] return arith1.CRT([(r, p) for (p, r) in list(log_mod_factor.items())])
def e2_Fp(x, p): """ p is prime f = x[0] + x[1]*t + x[2]*t**2 """ c, b, a = [_x % p for _x in x] if a == 0: return [e1_ZnZ([c, b], p)] if p == 2: solutions = [] if x[0] & 1 == 0: solutions.append(0) if (x[0] + x[1] + x[2]) & 1 == 0: solutions.append(1) if len(solutions) == 1: return solutions * 2 return solutions d = b**2 - 4 * a * c if arith1.legendre(d, p) == -1: return [] sqrtd = arith1.modsqrt(d, p) a = arith1.inverse(2 * a, p) return [((-b + sqrtd) * a) % p, ((-b - sqrtd) * a) % p]
def e2_Fp(x, p): """ p is prime f = x[0] + x[1]*t + x[2]*t**2 """ c, b, a = [_x % p for _x in x] if a == 0: return [e1_ZnZ([c, b], p)] if p == 2: solutions = [] if x[0] % 2 == 0: solutions.append(0) if (x[0] + x[1] + x[2]) % 2 == 0: solutions.append(1) if len(solutions) == 1: return solutions * 2 return solutions d = b**2 - 4*a*c if arith1.legendre(d, p) == -1: return [] sqrtd = arith1.modsqrt(d, p) a = arith1.inverse(2*a, p) return [((-b+sqrtd)*a)%p, ((-b-sqrtd)*a)%p]
def run_sieve(self): self.make_poly() T = time.time() M = self.Srange a = self.a_list[-1] # b = self.b_list[-1] # These are coefficients of F(x) c = (b**2-self.number)/(4*a) # d = self.d_list[-1] # self.poly_table = [] # This is F(x) value , x in [-M,M]. self.log_poly = [] # This is log(F(x)) value. self.minus_check = [] # This is "x" that F(x) is minus value. for j in self.move_range: jj = (a * j + b) * j + c if jj < 0: jj = -jj self.minus_check.append(j + M) elif jj == 0: jj = 1 lj = int((math.log(jj)*30)*0.95) # 0.95 is an erroe self.poly_table.append(jj) self.log_poly.append(lj) y = arith1.inverse(2*d, self.number) start_location = [] logp = [0]*(2*M+1) j = 2 for i in self.solution: start_p = [] ppow = 1 for k in range(len(i)): ppow *= self.FB[j] q = -M // ppow s_1 = (q + 1) * ppow + i[k][0] s_2 = (q + 1) * ppow + i[k][1] while s_1 + M >= ppow: s_1 = s_1 - ppow while s_2 + M >= ppow: s_2 = s_2 - ppow start_p.append([s_1+M, s_2+M]) start_location.append(start_p) j += 1 self.start_location = start_location i = self.poly_table[0] % 2 while i <= 2*M: j = 1 while self.poly_table[i] % (2**(j+1)) == 0: j += 1 logp[i] += self.FB_log[1]*j i += 2 L = 2 for plocation in self.start_location: for k in range(len(plocation)): s_1 = plocation[k][0] s_2 = plocation[k][1] ppow = self.FB[L]**(k+1) while s_1 <= 2*M: logp[s_1] += self.FB_log[L] s_1 += ppow while s_2 <= 2*M: logp[s_2] += self.FB_log[L] s_2 += ppow L += 1 self.logp = logp smooth = [] for t in range(2*M+1): if logp[t] >= self.log_poly[t]: poly_val = self.poly_table[t] index_vector = [] H = (y*(2*a*(t-self.Srange)+b))%self.number for p in self.FB: if p == -1: if t in self.minus_check: index_vector.append(1) else: index_vector.append(0) else: r = 0 while poly_val % (p**(r+1)) == 0: r += 1 v = r % 2 index_vector.append(v) smooth.append([index_vector, (poly_val, H)]) self.sievingtime += time.time() - T return smooth
def testInverse(self): self.assertEqual(205, arith1.inverse(160, 841)) self.assertEqual(1, arith1.inverse(1, 2**19 - 1)) self.assertRaises(ZeroDivisionError, arith1.inverse, 0, 3)