def gpol(g1,g2): """ Return G-Polynomial of ``g_1`` and ``g_2``. Let `a_i t_i` be `LT(g_i)`, `a = a_i*c_i + a_j*c_j` with `a = GCD(a_i,a_j)`, and `s_i = t/t_i` with `t = LCM(t_i,t_j)`. Then the G-Polynomial is defined as: `c_1s_1g_1 - c_2s_2g_2`. INPUT: - ``g1`` - polynomial - ``g2`` - polynomial EXAMPLE:: sage: from sage.rings.polynomial.toy_d_basis import gpol sage: P.<x, y, z> = PolynomialRing(IntegerRing(), 3, order='lex') sage: f = x^2 - 1 sage: g = 2*x*y - z sage: gpol(f,g) x^2*y - y """ a1,a2 = g1.lc(),g2.lc() a, c1, c2 = xgcd(a1,a2) t1,t2 = g1.lm(), g2.lm() t = t1.parent().monomial_lcm(t1,t2) s1,s2 = t//t1, t//t2 return c1*s1*g1 + c2*s2*g2
def gpol(g1, g2): """ Return G-Polynomial of ``g_1`` and ``g_2``. Let `a_i t_i` be `LT(g_i)`, `a = a_i*c_i + a_j*c_j` with `a = GCD(a_i,a_j)`, and `s_i = t/t_i` with `t = LCM(t_i,t_j)`. Then the G-Polynomial is defined as: `c_1s_1g_1 - c_2s_2g_2`. INPUT: - ``g1`` - polynomial - ``g2`` - polynomial EXAMPLE:: sage: from sage.rings.polynomial.toy_d_basis import gpol sage: P.<x, y, z> = PolynomialRing(IntegerRing(), 3, order='lex') sage: f = x^2 - 1 sage: g = 2*x*y - z sage: gpol(f,g) x^2*y - y """ a1, a2 = g1.lc(), g2.lc() a, c1, c2 = xgcd(a1, a2) t1, t2 = g1.lm(), g2.lm() t = t1.parent().monomial_lcm(t1, t2) s1, s2 = t // t1, t // t2 return c1 * s1 * g1 + c2 * s2 * g2
def BI(self, a, j): verbose('BI') K = self.parent().coefficient_module().base_ring() p = K.prime() G = self.parent().S_arithgroup() N = G.Gpn.level scale = 1 / self.Tq_eigenvalue(p) N = p * self.parent().S_arithgroup().level x = ZZ['x'].gen() prec = K.precision_cap() ans = 0 for q,_ in ZZ(N).factor(): if q == p: continue lambda_q = self.Tq_eigenvalue(q) order = Zmod(p)(q).multiplicative_order() scale /= (lambda_q - lambda_q**(1-order) * ZZ(q)**(order * j)) print 'scale = %s'%scale for m in range(order): new_ans = 0 for b in range(N): if (q**m * b - a) % p != 0: continue g, alpha0, beta = xgcd(b, -N) if g != 1: continue symb = self.evaluate(matrix(ZZ,2,2,[b, N, beta, alpha0])) new_ans += sum(ZZ(j).binomial(r) * b**(j-r) * N**r * symb.moment(r) for r in range(j+1)) ans += lambda_q**(-m) * q**(m*j) * new_ans return scale * ans
def atkin_lehner_matrix(self, Q): r""" Return the matrix of the Atkin--Lehner--Li operator `W_Q` associated to an exact divisor `Q` of `N`, where `N` is the level of this group; that is, `gcd(Q, N/Q) = 1`. .. note:: We follow the conventions of [AL1978]_ here, so `W_Q` is given by the action of any matrix of the form `\begin{pmatrix} Qx & y \\ Nz & Qw \end{pmatrix}` where `x,y,z,w` are integers such that `y = 1 \bmod Q`, `x = 1 \bmod N/Q`, and `det(W_Q) = Q`. For convenience, we actually always choose `x = y = 1`. INPUT: - ``Q`` (integer): an integer dividing `N`, where `N` is the level of this group. If this divisor does not satisfy `gcd(Q, N/Q) = 1`, it will be replaced by the unique integer with this property having the same prime factors as `Q`. EXAMPLES:: sage: Gamma1(994).atkin_lehner_matrix(71) [ 71 1] [4970 71] sage: Gamma1(996).atkin_lehner_matrix(2) [ 4 1] [-996 -248] sage: Gamma1(15).atkin_lehner_matrix(7) Traceback (most recent call last): ... ValueError: Q must divide the level """ # normalise Q Q = ZZ(Q) N = self.level() if not Q.divides(N): raise ValueError("Q must divide the level") Q = N // N.prime_to_m_part(Q) _, z, w = xgcd(-N // Q, Q) # so w * Q - z*(N/Q) = 1 return matrix(ZZ, 2, 2, [Q, 1, N * z, Q * w])
def BI_old(self, a, j): G = self.parent().S_arithgroup() N = G.Gpn.level p = G.prime() scale = 1 x = ZZ['x'].gen() for q,_ in ZZ(N).factor(): if q != p: lambda_q = self.get_liftee().Tq_eigenvalue(q) scale /= (1 - ZZ(q)**j / lambda_q) ans = 0 for alpha in range(N): if (alpha - a) % p != 0: continue g, alpha0, beta = xgcd(alpha, -N) if g != 1: continue symb = self.evaluate(matrix(ZZ,2,2,[alpha, beta, N, alpha0])) ans += -symb.evaluate_at_poly((alpha + N*(-x))**j) return scale * ans
def random_key(self, lbound, ubound, ntries=100): r""" Return a pair of random public and private keys. INPUT: - ``lbound`` -- positive integer; the lower bound on how small each random Blum prime `p` and `q` can be. So we have ``0 < lower_bound <= p, q <= upper_bound``. The lower bound must be distinct from the upper bound. - ``ubound`` -- positive integer; the upper bound on how large each random Blum prime `p` and `q` can be. So we have ``0 < lower_bound <= p, q <= upper_bound``. The lower bound must be distinct from the upper bound. - ``ntries`` -- (default: ``100``) the number of attempts to generate a random public/private key pair. If ``ntries`` is a positive integer, then perform that many attempts at generating a random public/private key pair. OUTPUT: - A random public key and its corresponding private key. Each randomly chosen `p` and `q` are guaranteed to be Blum primes. The public key is `n = pq`, and the private key is `(p, q, a, b)` where `\gcd(p, q) = ap + bq = 1`. ALGORITHM: The key generation algorithm is described in Algorithm 8.55, page 308 of [MvOV1996]_. The algorithm works as follows: #. Let `p` and `q` be distinct large random primes, each congruent to 3 modulo 4. That is, `p` and `q` are Blum primes. #. Let `n = pq` be the product of `p` and `q`. #. Use the extended Euclidean algorithm to compute integers `a` and `b` such that `\gcd(p, q) = ap + bq = 1`. #. The public key is `n` and the corresponding private key is `(p, q, a, b)`. .. NOTE:: Beware that there might not be any primes between the lower and upper bounds. So make sure that these two bounds are "sufficiently" far apart from each other for there to be primes congruent to 3 modulo 4. In particular, there should be at least two distinct primes within these bounds, each prime being congruent to 3 modulo 4. EXAMPLES: Choosing a random pair of public and private keys. We then test to see if they satisfy the requirements of the Blum-Goldwasser scheme:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import is_blum_prime sage: bg = BlumGoldwasser() sage: pubkey, prikey = bg.random_key(10**4, 10**5) sage: p, q, a, b = prikey sage: is_blum_prime(p); is_blum_prime(q) True True sage: p == q False sage: pubkey == p*q True sage: gcd(p, q) == a*p + b*q == 1 True TESTS: Make sure that there is at least one Blum prime between the lower and upper bounds. In the following example, we have ``lbound=24`` and ``ubound=30`` with 29 being the only prime within those bounds. But 29 is not a Blum prime. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: pubkey, privkey = bg.random_key(24, 30) Traceback (most recent call last): ... ValueError: No Blum primes within the specified closed interval. """ # choosing distinct random Blum primes p = random_blum_prime(lbound=lbound, ubound=ubound, ntries=ntries) q = random_blum_prime(lbound=lbound, ubound=ubound, ntries=ntries) while p == q: q = random_blum_prime(lbound=lbound, ubound=ubound, ntries=ntries) # compute the public key n = p * q # compute the private key; here gcd(p, q) = 1 = a*p + b*q bezout = xgcd(p, q) a = bezout[1] b = bezout[2] return (n, (p, q, a, b))
def private_key(self, p, q): r""" Return the Blum-Goldwasser private key corresponding to the distinct Blum primes ``p`` and ``q``. INPUT: - ``p`` -- a Blum prime. - ``q`` -- a Blum prime. OUTPUT: - The Blum-Goldwasser private key `(p, q, a, b)` where `\gcd(p, q) = ap + bq = 1`. Both ``p`` and ``q`` must be distinct Blum primes. Let `p` be a positive prime. Then `p` is a Blum prime if `p` is congruent to 3 modulo 4, i.e. `p \equiv 3 \pmod{4}`. EXAMPLES: Obtain two distinct Blum primes and compute the Blum-Goldwasser private key corresponding to those two Blum primes:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import is_blum_prime sage: bg = BlumGoldwasser() sage: P = primes_first_n(10); P [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] sage: [is_blum_prime(_) for _ in P] [False, True, False, True, True, False, False, True, True, False] sage: bg.private_key(19, 23) (19, 23, -6, 5) Choose two distinct random Blum primes, compute the Blum-Goldwasser private key corresponding to those two primes, and test that the resulting private key `(p, q, a, b)` satisfies `\gcd(p, q) = ap + bq = 1`:: sage: from sage.crypto.util import random_blum_prime sage: p = random_blum_prime(10**4, 10**5) sage: q = random_blum_prime(10**4, 10**5) sage: while q == p: ....: q = random_blum_prime(10**4, 10**5) sage: p, q, a, b = bg.private_key(p, q) sage: gcd(p, q) == a*p + b*q == 1 True TESTS: Both of the input ``p`` and ``q`` must be distinct Blum primes. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: bg.private_key(78307, 78307) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. sage: bg.private_key(7, 4) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. """ if p == q: raise ValueError("p and q must be distinct Blum primes.") if is_blum_prime(p) and is_blum_prime(q): # here gcd(p, q) = ap + bq = 1 bezout = xgcd(p, q) a = bezout[1] b = bezout[2] return (p, q, a, b) else: raise ValueError("p and q must be distinct Blum primes.")
def random_key(self, lbound, ubound, ntries=100): r""" Return a pair of random public and private keys. INPUT: - ``lbound`` -- positive integer; the lower bound on how small each random Blum prime `p` and `q` can be. So we have ``0 < lower_bound <= p, q <= upper_bound``. The lower bound must be distinct from the upper bound. - ``ubound`` -- positive integer; the upper bound on how large each random Blum prime `p` and `q` can be. So we have ``0 < lower_bound <= p, q <= upper_bound``. The lower bound must be distinct from the upper bound. - ``ntries`` -- (default: ``100``) the number of attempts to generate a random public/private key pair. If ``ntries`` is a positive integer, then perform that many attempts at generating a random public/private key pair. OUTPUT: - A random public key and its corresponding private key. Each randomly chosen `p` and `q` are guaranteed to be Blum primes. The public key is `n = pq`, and the private key is `(p, q, a, b)` where `\gcd(p, q) = ap + bq = 1`. ALGORITHM: The key generation algorithm is described in Algorithm 8.55, page 308 of [MenezesEtAl1996]_. The algorithm works as follows: #. Let `p` and `q` be distinct large random primes, each congruent to 3 modulo 4. That is, `p` and `q` are Blum primes. #. Let `n = pq` be the product of `p` and `q`. #. Use the extended Euclidean algorithm to compute integers `a` and `b` such that `\gcd(p, q) = ap + bq = 1`. #. The public key is `n` and the corresponding private key is `(p, q, a, b)`. .. NOTE:: Beware that there might not be any primes between the lower and upper bounds. So make sure that these two bounds are "sufficiently" far apart from each other for there to be primes congruent to 3 modulo 4. In particular, there should be at least two distinct primes within these bounds, each prime being congruent to 3 modulo 4. EXAMPLES: Choosing a random pair of public and private keys. We then test to see if they satisfy the requirements of the Blum-Goldwasser scheme:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import is_blum_prime sage: bg = BlumGoldwasser() sage: pubkey, prikey = bg.random_key(10**4, 10**5) sage: p, q, a, b = prikey sage: is_blum_prime(p); is_blum_prime(q) True True sage: p == q False sage: pubkey == p*q True sage: gcd(p, q) == a*p + b*q == 1 True TESTS: Make sure that there is at least one Blum prime between the lower and upper bounds. In the following example, we have ``lbound=24`` and ``ubound=30`` with 29 being the only prime within those bounds. But 29 is not a Blum prime. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: pubkey, privkey = bg.random_key(24, 30) Traceback (most recent call last): ... ValueError: No Blum primes within the specified closed interval. """ # choosing distinct random Blum primes p = random_blum_prime(lbound=lbound, ubound=ubound, ntries=ntries) q = random_blum_prime(lbound=lbound, ubound=ubound, ntries=ntries) while p == q: q = random_blum_prime(lbound=lbound, ubound=ubound, ntries=ntries) # compute the public key n = p * q # compute the private key; here gcd(p, q) = 1 = a*p + b*q bezout = xgcd(p, q) a = bezout[1] b = bezout[2] return (n, (p, q, a, b))
def private_key(self, p, q): r""" Return the Blum-Goldwasser private key corresponding to the distinct Blum primes ``p`` and ``q``. INPUT: - ``p`` -- a Blum prime. - ``q`` -- a Blum prime. OUTPUT: - The Blum-Goldwasser private key `(p, q, a, b)` where `\gcd(p, q) = ap + bq = 1`. Both ``p`` and ``q`` must be distinct Blum primes. Let `p` be a positive prime. Then `p` is a Blum prime if `p` is congruent to 3 modulo 4, i.e. `p \equiv 3 \pmod{4}`. EXAMPLES: Obtain two distinct Blum primes and compute the Blum-Goldwasser private key corresponding to those two Blum primes:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import is_blum_prime sage: bg = BlumGoldwasser() sage: P = primes_first_n(10); P [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] sage: [is_blum_prime(_) for _ in P] [False, True, False, True, True, False, False, True, True, False] sage: bg.private_key(19, 23) (19, 23, -6, 5) Choose two distinct random Blum primes, compute the Blum-Goldwasser private key corresponding to those two primes, and test that the resulting private key `(p, q, a, b)` satisfies `\gcd(p, q) = ap + bq = 1`:: sage: from sage.crypto.util import random_blum_prime sage: p = random_blum_prime(10**4, 10**5) sage: q = random_blum_prime(10**4, 10**5) sage: while q == p: ... q = random_blum_prime(10**4, 10**5) ... sage: p, q, a, b = bg.private_key(p, q) sage: gcd(p, q) == a*p + b*q == 1 True TESTS: Both of the input ``p`` and ``q`` must be distinct Blum primes. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: bg.private_key(78307, 78307) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. sage: bg.private_key(7, 4) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. """ if p == q: raise ValueError("p and q must be distinct Blum primes.") if is_blum_prime(p) and is_blum_prime(q): # here gcd(p, q) = ap + bq = 1 bezout = xgcd(p, q) a = bezout[1] b = bezout[2] return (p, q, a, b) else: raise ValueError("p and q must be distinct Blum primes.")