Example #1
0
def solve_linear_congruence(a, b, c, n):
    """Returns a solution to ax+by=c mod n."""
    a = Integer(a)
    b = Integer(b)
    x = Integer(c)
    n = Integer(n)
    x = ZZ.random_element(0, n)
    y = (c - a * x) * ~mod(b, n)
    y = Integer(mod(y, n))
    assert mod(a * x + b * y, n) == mod(c, n)
    return x, y
Example #2
0
def center_around(x, center, modulus):
    """Returns x' = x mod modulus in the interval
    [center - modulus / 2, center + modulus / 2].
    """
    x = Integer(x)
    modulus = Integer(modulus)
    k = ceil((center - modulus / 2 - x) / modulus)
    x_prime = x + modulus * k

    assert mod(x, modulus) == mod(x_prime, modulus)
    assert center - modulus / 2 <= x_prime <= center + modulus / 2
    return x_prime
Example #3
0
def finding_t_modln(E, l, volcano_2=True):
    field = E.base_field()
    q = E.base_field().order()
    j = E.j_invariant()

    if l == 2:
        mod_l = trace_of_frobenius_mod_2(E)
    else:
        mod_l = trace_of_frobenius_mod(E, l)

    # computes depth of volcano modulo l
    if mod(mod_l**2, l) != mod(4 * q, l):
        return mod_l, l
    try:
        result = find_full_descending_paths(j, l)
    except Found_0_1728:
        return mod_l, l
    if len(result) == 1:
        return mod_l, l

    n = result[3]
    e = result[2]

    modul = l**(2 * n)
    if e == 0:
        modul *= l
    if l == 2 and not volcano_2:
        if modul < 2**3:
            return mod_l, 2
        else:
            return 2, 4

    if l == 2:
        if modul < 2**3:
            return mod_l, 2
        if modul < 2**5 or q % 8 == 3 or q % 8 == 7:
            return 2, 4
        roots = mod(4 * q, modul).sqrt(all=True)
        roots = [Integer(r % (modul // 4)) for r in roots]
        if is_8_order(E):
            for r in roots:
                if q % 8 == 1 and r % 8 == 2:
                    return r, Integer(modul // 4)
                if q % 8 == 5 and r % 8 == 6:
                    return r, Integer(modul // 4)
        else:
            for r in roots:
                if q % 8 == 5 and r % 8 == 2:
                    return r, Integer(modul // 4)
                if q % 8 == 1 and r % 8 == 6:
                    return r, Integer(modul // 4)

    root = Integer(mod(4 * q, modul).sqrt())

    if mod(root, l) == mod(mod_l, l):
        return root, modul
    else:
        return modul - root, modul
Example #4
0
def _brown_indecomposable(q, p):
    r"""
    Return the Brown invariant of the indecomposable form ``q``.

    The values are taken from Table 2.1 in [Shim2016]_.

    INPUT:

    - ``q`` - an indecomposable quadratic form represented by a
      rational `1 \times 1` or `2 \times 2` matrix
    - ``p`` - a prime number

    EXAMPLES::

        sage: from sage.modules.torsion_quadratic_module import _brown_indecomposable
        sage: q = Matrix(QQ, [1/3])
        sage: _brown_indecomposable(q,3)
        6
        sage: q = Matrix(QQ, [2/3])
        sage: _brown_indecomposable(q,3)
        2
        sage: q = Matrix(QQ, [5/4])
        sage: _brown_indecomposable(q,2)
        5
        sage: q = Matrix(QQ, [7/4])
        sage: _brown_indecomposable(q,2)
        7
        sage: q = Matrix(QQ, 2, [0,1,1,0])/2
        sage: _brown_indecomposable(q,2)
        0
        sage: q = Matrix(QQ, 2, [2,1,1,2])/2
        sage: _brown_indecomposable(q,2)
        4
    """
    v = q.denominator().valuation(p)
    if p == 2:
        # brown(U) = 0
        if q.ncols() == 2:
            if q[0, 0].valuation(2) > v + 1 and q[1, 1].valuation(2) > v + 1:
                # type U
                return mod(0, 8)
            else:
                # type V
                return mod(4 * v, 8)
        u = q[0, 0].numerator()
        return mod(u + v * (u**2 - 1) / 2, 8)
    if p % 4 == 1:
        e = -1
    if p % 4 == 3:
        e = 1
    if v % 2 == 1:
        u = q[0, 0].numerator() // 2
        if legendre_symbol(u, p) == 1:
            return mod(1 + e, 8)
        else:
            return mod(-3 + e, 8)
    return mod(0, 8)
def _brown_indecomposable(q, p):
    r"""
    Return the Brown invariant of the indecomposable form ``q``.

    The values are taken from Table 2.1 in [Shim2016]_.

    INPUT:

    - ``q`` - an indecomposable quadratic form represented by a
      rational `1 \times 1` or `2 \times 2` matrix
    - ``p`` - a prime number

    EXAMPLES::

        sage: from sage.modules.torsion_quadratic_module import _brown_indecomposable
        sage: q = Matrix(QQ, [1/3])
        sage: _brown_indecomposable(q,3)
        6
        sage: q = Matrix(QQ, [2/3])
        sage: _brown_indecomposable(q,3)
        2
        sage: q = Matrix(QQ, [5/4])
        sage: _brown_indecomposable(q,2)
        5
        sage: q = Matrix(QQ, [7/4])
        sage: _brown_indecomposable(q,2)
        7
        sage: q = Matrix(QQ, 2, [0,1,1,0])/2
        sage: _brown_indecomposable(q,2)
        0
        sage: q = Matrix(QQ, 2, [2,1,1,2])/2
        sage: _brown_indecomposable(q,2)
        4
    """
    v = q.denominator().valuation(p)
    if p == 2:
        # brown(U) = 0
        if q.ncols() == 2:
            if q[0,0].valuation(2)>v+1 and q[1,1].valuation(2)>v+1:
                # type U
                return mod(0, 8)
            else:
                # type V
                return mod(4*v, 8)
        u = q[0,0].numerator()
        return mod(u + v*(u**2 - 1)/2, 8)
    if p % 4 == 1:
        e = -1
    if p % 4 == 3:
        e = 1
    if v % 2 == 1:
        u = q[0,0].numerator()//2
        if legendre_symbol(u,p) == 1:
            return mod(1 + e, 8)
        else:
            return mod(-3 + e, 8)
    return mod(0, 8)
Example #6
0
def is_blum_prime(n):
    r"""
    Determine whether or not ``n`` is a Blum prime.

    INPUT:

    - ``n`` a positive prime.

    OUTPUT:

    - ``True`` if ``n`` is a Blum prime; ``False`` otherwise.

    Let `n` be a positive prime. Then `n` is a Blum prime if `n` is
    congruent to 3 modulo 4, i.e. `n \equiv 3 \pmod{4}`.

    EXAMPLES:

    Testing some integers to see if they are Blum primes::

        sage: from sage.crypto.util import is_blum_prime
        sage: from sage.crypto.util import random_blum_prime
        sage: is_blum_prime(101)
        False
        sage: is_blum_prime(7)
        True
        sage: p = random_blum_prime(10**3, 10**5)
        sage: is_blum_prime(p)
        True
    """
    if n < 0:
        return False
    if is_prime(n):
        if mod(n, 4).lift() == 3:
            return True
        else:
            return False
    else:
        return False
Example #7
0
def is_blum_prime(n):
    r"""
    Determine whether or not ``n`` is a Blum prime.

    INPUT:

    - ``n`` a positive prime.

    OUTPUT:

    - ``True`` if ``n`` is a Blum prime; ``False`` otherwise.

    Let `n` be a positive prime. Then `n` is a Blum prime if `n` is
    congruent to 3 modulo 4, i.e. `n \equiv 3 \pmod{4}`.

    EXAMPLES:

    Testing some integers to see if they are Blum primes::

        sage: from sage.crypto.util import is_blum_prime
        sage: from sage.crypto.util import random_blum_prime
        sage: is_blum_prime(101)
        False
        sage: is_blum_prime(7)
        True
        sage: p = random_blum_prime(10**3, 10**5)
        sage: is_blum_prime(p)
        True
    """
    if n < 0:
        return False
    if is_prime(n):
        if mod(n, 4).lift() == 3:
            return True
        else:
            return False
    else:
        return False
Example #8
0
def ell_power_equiv(J, O, ell, print_progress=False):
    """Solve ell isogeny problem.

    This function only works in quaternion algebras with prime discriminant
    p = 3 mod 4.

    Args:
        J: A left O-ideal.
        O: An order in a quaternion algebra.
        ell: A prime.
        print_progress: True if you want to print progress.

    Returns:
        A pair (J, delta) where J = I*delta, delta is in the quaternion algebra
        and J is a nonfractional ideal in the same class as I that has ell
        power norm.
    """
    B = O.quaternion_algebra()
    if not is_prime(B.discriminant()) or not mod(B.discriminant(), 4) == 3:
        raise NotImplementedError("The quaternion algebra must have prime"
                                  " discrimint p = 3 mod 4.")

    # When B has discriminant p = 3 mod 4 the call B.maximal_order() always
    # returns the first maximal order in the cases environment in Lemma 2 of
    # the paper. I probably shouldn't rely on this.
    O_special = B.maximal_order()
    I = connecting_ideal(O_special, O)
    K = I * J
    I_1, gamma_1 = special_ell_power_equiv(I, O_special, ell)
    I_2, gamma_2 = special_ell_power_equiv(K, O_special, ell)
    gamma = gamma_1.conjugate() * gamma_2 * I.norm()
    J_2 = J.scale(gamma)
    assert J_2.left_order() == O
    assert Integer(J_2.norm()).prime_factors() == [ell]
    assert [x in O for x in J_2.basis()]
    return J_2, gamma
Example #9
0
def is_semisimple_modular(M, m, nprimes=5):
    """M is a pari matrix over Q(zeta_m).  Check if M mod p has squarefree
    char poly for nprimes primes p=1 (mod m).  If True for any p,
    return True since then the char poly of M itself must be
    square-free.  If False for all p, return False since the M
    probably has non-squarefree char poly.  There may therefore be
    false negatives.
    """
    pol = cyclotomic_polynomial(m)
    pt = pari("t")
    np = 0
    for p in prime_range(1000000):
        if m > 1 and not p % m == 1:
            continue
        np += 1
        #print("testing modulo p={}".format(p))
        if np > nprimes:
            #print("op not semisimple modulo {} primes, so returning False".format(nprimes))
            return False
        zmodp = pari(pol.roots(GF(p))[0][0])
        #print("zmodp = {}".format(zmodp))
        try:
            Mmodp = M.lift() * pari(mod(1, p))
            #print("Lifted matrix = {}".format(Mmodp))
            Mmodp = Mmodp.subst(pt, zmodp)
            #print("matrix (mod {}) = {}".format(p,Mmodp))
            modpcharpoly = Mmodp.charpoly()
            #print("char poly (mod {}) = {}".format(p,modpcharpoly))
            if modpcharpoly.issquarefree():
                #print("op is semisimple mod {}".format(p))
                return True
            else:
                #print("op is not semisimple mod {}".format(p))
                pass
        except PariError:  ## happens if M not integral mod p
            np -= 1
Example #10
0
def _normalize(G, normal_odd=True):
    r"""
    Return the transformation to sums of forms of types `U`, `V` and `W`.

    Part of the algorithm :meth:`p_adic_normal_form`.

    INPUT:

    - ``G`` -- a symmetric matrix over `\ZZ_p` in jordan form --
      the output of :meth:`p_adic_normal_form` or :meth:`_jordan_2_adic`
    - ``normal_odd`` -- bool (default: True) if true and `p` is odd,
      compute a normal form.

    OUTPUT:

    - ``(D, B)`` -- a pair of matrices such that ``D=B*G*B.T``
      is a sum of forms of types `U`, `V` and `W` for `p=2` or
      diagonal with diagonal entries equal `1` or `u`
      where `u` is the smallest non-square modulo the odd prime `p`.

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _normalize
        sage: R = Zp(3, prec = 5, type = 'fixed-mod', print_mode='series', show_prec=False)
        sage: G = matrix.diagonal(R, [1,7,3,3*5,3,9,-9,27*13])
        sage: D, B =_normalize(G)
        sage: D
        [    1     0     0     0     0     0     0     0]
        [    0     1     0     0     0     0     0     0]
        [    0     0     3     0     0     0     0     0]
        [    0     0     0     3     0     0     0     0]
        [    0     0     0     0   2*3     0     0     0]
        [    0     0     0     0     0   3^2     0     0]
        [    0     0     0     0     0     0 2*3^2     0]
        [    0     0     0     0     0     0     0   3^3]
    """
    R = G.base_ring()
    D = copy(G)
    p = R.prime()
    n = G.ncols()
    B = copy(G.parent().identity_matrix())
    if p != 2:
        # squareclasses 1, v
        v = _min_nonsquare(p)
        v = R(v)
        non_squares = []
        val = 0
        for i in range(n):
            if D[i,i].valuation() > val:
                # a new block starts
                val = D[i,i].valuation()
                if normal_odd and len(non_squares) != 0:
                    # move the non-square to
                    # the last entry of the previous block
                    j = non_squares.pop()
                    B.swap_rows(j, i-1)
            d = D[i, i].unit_part()
            if d.is_square():
                D[i, i] = 1
                B[i, :] *= d.inverse_of_unit().sqrt()
            else:
                D[i, i] = v
                B[i, :] *= (v * d.inverse_of_unit()).sqrt()
                if normal_odd and len(non_squares) != 0:
                    # we combine two non-squares to get
                    # the 2 x 2 identity matrix
                    j = non_squares.pop()
                    trafo = _normalize_odd_2x2(D[[i,j],[i,j]])
                    B[[i,j],:] = trafo*B[[i,j],:]
                    D[i,i] = 1
                    D[j,j] = 1
                else:
                    non_squares.append(i)
        if normal_odd and len(non_squares) != 0:
            j=non_squares.pop()
            B.swap_rows(j,n-1)
    else:
        # squareclasses 1,3,5,7 modulo 8
        for i in range(n):
            d = D[i, i].unit_part()
            if d != 0:
                v = R(mod(d, 8))
                B[i, :] *= (v * d.inverse_of_unit()).sqrt()
        D = B * G * B.T
        for i in range(n-1):
            if D[i, i+1] != 0:    # there is a 2 x 2 block here
                block = D[i:i+2, i:i+2]
                trafo = _normalize_2x2(block)
                B[i:i+2, :] = trafo * B[i:i+2, :]
    D = B * G * B.T
    return D, B
Example #11
0
def strong_approximation(mu_0, N, O, ell):
    """Find mu in O with nrd(mu) = ell^e and mu = lambda * mu_0 mod NO

    Args:
        mu_0: An element of Rj.
        N: A prime.
        O: An order contaning 1, i, j, k.
        ell: A prime.

    Returns:
        mu in O with nrd(mu) = ell^e and mu = lambda * mu_0 mod NO.
    """
    ell = Integer(ell)
    N = Integer(N)
    B = O.quaternion_algebra()
    p = B.discriminant()
    i, j, k = B.gens()
    t_0, x_0, y_0, z_0 = mu_0.coefficient_tuple()
    assert t_0 == x_0 == 0
    beta_0 = y_0 + z_0 * i
    # TODO: gracefully handle the case where
    # ~mod(p * Integer(beta_0.reduced_norm()), N) does not exist.
    e = 2 * ceil(log(N**4 * (p + 1) / 2, ell)) + (0 if (
        ~mod(p * Integer(beta_0.reduced_norm()), N)).is_square() else 1)
    e_max = e + 2 * (2 * ceil(log(p, ell)) + 2)

    # First we solve for lambda.
    lamb = Integer(
        (ell**e * ~mod(p * Integer(beta_0.reduced_norm()), N)).sqrt())
    assert mod(ell**e, N) == mod(lamb**2 * Integer(mu_0.reduced_norm()), N)

    mu = None
    count = 0
    count_max = 5 * e
    while e < e_max:
        # Then we solve for beta_1.
        lhs = Integer(
            (ell**e - p * lamb**2 * Integer(beta_0.reduced_norm())) / N)
        y_1, z_1 = solve_linear_congruence(2 * Integer(y_0) * p * lamb,
                                           p * lamb * 2 * z_0, lhs, N)
        y_1 = center_around(y_1, -2 * lamb * y_0, N)
        z_1 = center_around(z_1, -2 * lamb * y_0, N)
        beta_1 = Integer(y_1) + Integer(z_1) * i
        assert mod(lhs, N) == mod(
            p * lamb * (beta_0 * beta_1.conjugate()).reduced_trace(), N)

        # Now we calculate r.
        r = Integer(
            (ell**e - p * (lamb * beta_0 + N * beta_1).reduced_norm()) / N**2)

        # In the paper they say that r can be the product of a prime and a
        # smooth square. For simplicity I will just wait for r prime.
        if not is_prime(r):
            count += 1
            if count > count_max:
                print("Increasing", e, e_max)
                e += 2
                count_max = 0

            continue

        sol = solve_norm_equation(1, r)
        if sol is not None:
            t_1, x_1 = sol
            alpha_1 = t_1 + x_1 * i
            mu_1 = alpha_1 + beta_1 * j
            mu = lamb * mu_0 + N * mu_1
            assert mu - lamb * mu_0 in O.left_ideal(O.basis()).scale(N)
            assert mu.reduced_norm() == ell**e
            return mu

        # This is sort of a random heuristic. Can do better.
        count += 1
        if count > count_max:
            e += 2
            count_max = 0

    return None
Example #12
0
def TaylorTwographDescendantSRG(q, clique_partition=None):
    r"""
    constructing the descendant graph of the Taylor's two-graph for `U_3(q)`, `q` odd

    This is a strongly regular graph with parameters
    `(v,k,\lambda,\mu)=(q^3, (q^2+1)(q-1)/2, (q-1)^3/4-1, (q^2+1)(q-1)/4)`
    obtained as a two-graph descendant of the
    :func:`Taylor's two-graph <sage.combinat.designs.twographs.taylor_twograph>` `T`.
    This graph admits a partition into cliques of size `q`, which are useful in
    :func:`TaylorTwographSRG <sage.graphs.generators.classical_geometries.TaylorTwographSRG>`,
    a strongly regular graph on `q^3+1` vertices in the
    Seidel switching class of `T`, for which we need `(q^2+1)/2` cliques.
    The cliques are the `q^2` lines on `v_0` of the projective plane containing the unital
    for `U_3(q)`, and intersecting the unital (i.e. the vertices of the graph and the point
    we remove) in `q+1` points. This is all taken from §7E of [BvL84]_.

    INPUT:

    - ``q`` -- a power of an odd prime number

    - ``clique_partition`` -- if ``True``, return `q^2-1` cliques of size `q`
      with empty pairwise intersection. (Removing all of them leaves a clique, too),
      and the point removed from the unital.

    EXAMPLES::

        sage: g=graphs.TaylorTwographDescendantSRG(3); g
        Taylor two-graph descendant SRG: Graph on 27 vertices
        sage: g.is_strongly_regular(parameters=True)
        (27, 10, 1, 5)
        sage: from sage.combinat.designs.twographs import taylor_twograph
        sage: T = taylor_twograph(3)                           # long time
        sage: g.is_isomorphic(T.descendant(T.ground_set()[1])) # long time
        True
        sage: g=graphs.TaylorTwographDescendantSRG(5)    # not tested (long time)
        sage: g.is_strongly_regular(parameters=True)  # not tested (long time)
        (125, 52, 15, 26)

    TESTS::

        sage: g,l,_=graphs.TaylorTwographDescendantSRG(3,clique_partition=True)
        sage: all(map(lambda x: g.is_clique(x), l))
        True
        sage: graphs.TaylorTwographDescendantSRG(4)
        Traceback (most recent call last):
        ...
        ValueError: q must be an odd prime power
        sage: graphs.TaylorTwographDescendantSRG(6)
        Traceback (most recent call last):
        ...
        ValueError: q must be an odd prime power
    """
    from sage.rings.arith import is_prime_power
    p, k = is_prime_power(q, get_data=True)
    if k == 0 or p == 2:
        raise ValueError('q must be an odd prime power')
    from sage.schemes.projective.projective_space import ProjectiveSpace
    from sage.rings.finite_rings.constructor import FiniteField
    from sage.modules.free_module_element import free_module_element as vector
    from sage.rings.finite_rings.integer_mod import mod
    from __builtin__ import sum
    Fq = FiniteField(q**2, 'a')
    PG = map(tuple, ProjectiveSpace(2, Fq))

    def S(x, y):
        return sum(map(lambda j: x[j] * y[2 - j]**q, xrange(3)))

    V = filter(lambda x: S(x, x) == 0, PG)  # the points of the unital
    v0 = V[0]
    V.remove(v0)
    if mod(q, 4) == 1:
        G = Graph(
            [V, lambda y, z: not (S(v0, y) * S(y, z) * S(z, v0)).is_square()],
            loops=False)
    else:
        G = Graph(
            [V, lambda y, z: (S(v0, y) * S(y, z) * S(z, v0)).is_square()],
            loops=False)
    G.name("Taylor two-graph descendant SRG")
    if clique_partition:
        lines = map(lambda x: filter(lambda t: t[0] + x * t[1] == 0, V),
                    filter(lambda z: z != 0, Fq))
        return (G, lines, v0)
    else:
        return G
Example #13
0
def bin_to_ascii(B):
    r"""
    Return the ASCII representation of the binary string ``B``.

    INPUT:

    - ``B`` -- a non-empty binary string or a non-empty list of bits. The
      number of bits in ``B`` must be a multiple of 8.

    OUTPUT:

    - The ASCII string corresponding to ``B``.

    ALGORITHM:

    Consider a block of bits `B = b_0 b_1 \cdots b_{n-1}` where each
    sub-block `b_i` is a binary string of length 8. Then the total number
    of bits is a multiple of 8 and is given by `8n`. Let `c_i` be the
    integer representation of `b_i`. We can consider `c_i` as the integer
    representation of an ASCII character. Then the ASCII representation
    `A` of `B` is `A = a_0 a_1 \cdots a_{n-1}`.

    EXAMPLES:

    Convert some ASCII strings to their binary representations and recover
    the ASCII strings from the binary representations::

        sage: from sage.crypto.util import ascii_to_bin
        sage: from sage.crypto.util import bin_to_ascii
        sage: A = "Abc"
        sage: B = ascii_to_bin(A); B
        010000010110001001100011
        sage: bin_to_ascii(B)
        'Abc'
        sage: bin_to_ascii(B) == A
        True

    ::

        sage: A = "123 \" #"
        sage: B = ascii_to_bin(A); B
        00110001001100100011001100100000001000100010000000100011
        sage: bin_to_ascii(B)
        '123 " #'
        sage: bin_to_ascii(B) == A
        True

    This function also accepts strings and lists of bits::

        sage: from sage.crypto.util import bin_to_ascii
        sage: bin_to_ascii("010000010110001001100011")
        'Abc'
        sage: bin_to_ascii([0, 1, 0, 0, 0, 0, 0, 1])
        'A'

    TESTS:

    The number of bits in ``B`` must be non-empty and a multiple of 8::

        sage: from sage.crypto.util import bin_to_ascii
        sage: bin_to_ascii("")
        Traceback (most recent call last):
        ...
        ValueError: B must be a non-empty binary string.
        sage: bin_to_ascii([])
        Traceback (most recent call last):
        ...
        ValueError: B must be a non-empty binary string.
        sage: bin_to_ascii(" ")
        Traceback (most recent call last):
        ...
        ValueError: The number of bits in B must be a multiple of 8.
        sage: bin_to_ascii("101")
        Traceback (most recent call last):
        ...
        ValueError: The number of bits in B must be a multiple of 8.
        sage: bin_to_ascii([1, 0, 1])
        Traceback (most recent call last):
        ...
        ValueError: The number of bits in B must be a multiple of 8.
    """
    # sanity checks
    n = len(B)
    if n == 0:
        raise ValueError("B must be a non-empty binary string.")
    if mod(n, 8) != 0:
        raise ValueError("The number of bits in B must be a multiple of 8.")
    # perform conversion to ASCII string
    b = [int(str(x)) for x in list(B)]
    A = []
    # the number of 8-bit blocks
    k = n // 8
    for i in range(k):
        # Convert from 8-bit string to ASCII integer. Then convert the
        # ASCII integer to the corresponding ASCII character.
        A.append(chr(ascii_integer(b[8*i: 8*(i+1)])))
    return "".join(A)
Example #14
0
def prime_norm_representative(I, O, D, ell):
    """
    Given an order O and a left O-ideal I return another
    left O-ideal J in the same class, but with prime norm.

    This corresponds to Step 1 in the notes. So given an ideal I it returns
    an ideal in the same class but with reduced norm N where N != ell is
    a large prime coprime to both D and p, and ell is a quadratic
    nonresidue module N.


    Args:
        I: A left O-ideal.
        O: An order in a quaternion algebra.
        D: An integer.
        ell: A prime.
    Returns:
        A pair (J, gamma) where J = I * gamma is a left O-ideal in the same
        class with prime norm N. N will be coprime to both D and p, and ell
        will be a nonquadratic residue module N.
    """
    # TODO: Change so O is not an argument.
    if not is_minkowski_basis(I.basis()):
        print("Warning: The ideal I does not have a minkowski basis"
              " precomputed and Sage can not do it for you.")

    nrd_I = I.norm()
    B = I.quaternion_algebra()
    p = B.discriminant()
    alpha = B(0)
    normalized_norm = Integer(alpha.reduced_norm() / nrd_I)
    # Choose random elements in I until one is found with norm N*nrd(I) where N
    # is prime.
    m_power = 3
    m = Integer(2)**m_power  # TODO: Change this to a proper bound.
    count = 0
    while (not is_prime(normalized_norm) or normalized_norm.divides(D)
           or normalized_norm == ell or normalized_norm == p
           or mod(ell, normalized_norm).is_square()):
        # Make a new random element.
        alpha = random_combination(I.basis(), bound=m)
        normalized_norm = Integer(alpha.reduced_norm() / nrd_I)

        # Increase the box we search in if we've been trying for too long. Note
        # this was just a random heuristic I came up with, it's not in the
        # paper.
        count += 1
        if count > 4 * m_power:
            m_power += 1
            m = Integer(2)**m_power
            count = 0

    # We now have an element alpha with norm N*nrd(I) where N is prime. The
    # ideal J = I*gamma has prime norm where gamma = conjugate(alpha) / nrd(I).
    gamma = alpha.conjugate() / nrd_I
    J = I.scale(gamma)

    assert is_prime(Integer(J.norm()))
    assert not mod(ell, Integer(J.norm())).is_square()
    assert gcd(Integer(J.norm()), D) == 1
    return J, gamma
Example #15
0
def TaylorTwographDescendantSRG(q, clique_partition=None):
    r"""
    constructing the descendant graph of the Taylor's two-graph for `U_3(q)`, `q` odd

    This is a strongly regular graph with parameters
    `(v,k,\lambda,\mu)=(q^3, (q^2+1)(q-1)/2, (q-1)^3/4-1, (q^2+1)(q-1)/4)`
    obtained as a two-graph descendant of the
    :func:`Taylor's two-graph <sage.combinat.designs.twographs.taylor_twograph>` `T`.
    This graph admits a partition into cliques of size `q`, which are useful in
    :func:`~sage.graphs.graph_generators.GraphGenerators.TaylorTwographSRG`,
    a strongly regular graph on `q^3+1` vertices in the
    Seidel switching class of `T`, for which we need `(q^2+1)/2` cliques.
    The cliques are the `q^2` lines on `v_0` of the projective plane containing the unital
    for `U_3(q)`, and intersecting the unital (i.e. the vertices of the graph and the point
    we remove) in `q+1` points. This is all taken from §7E of [BvL84]_.

    INPUT:

    - ``q`` -- a power of an odd prime number

    - ``clique_partition`` -- if ``True``, return `q^2-1` cliques of size `q`
      with empty pairwise intersection. (Removing all of them leaves a clique, too),
      and the point removed from the unital.

    EXAMPLES::

        sage: g=graphs.TaylorTwographDescendantSRG(3); g
        Taylor two-graph descendant SRG: Graph on 27 vertices
        sage: g.is_strongly_regular(parameters=True)
        (27, 10, 1, 5)
        sage: from sage.combinat.designs.twographs import taylor_twograph
        sage: T = taylor_twograph(3)                           # long time
        sage: g.is_isomorphic(T.descendant(T.ground_set()[1])) # long time
        True
        sage: g=graphs.TaylorTwographDescendantSRG(5)    # not tested (long time)
        sage: g.is_strongly_regular(parameters=True)  # not tested (long time)
        (125, 52, 15, 26)

    TESTS::

        sage: g,l,_=graphs.TaylorTwographDescendantSRG(3,clique_partition=True)
        sage: all(map(lambda x: g.is_clique(x), l))
        True
        sage: graphs.TaylorTwographDescendantSRG(4)
        Traceback (most recent call last):
        ...
        ValueError: q must be an odd prime power
        sage: graphs.TaylorTwographDescendantSRG(6)
        Traceback (most recent call last):
        ...
        ValueError: q must be an odd prime power
    """
    p, k = is_prime_power(q,get_data=True)
    if k==0 or p==2:
       raise ValueError('q must be an odd prime power')
    from sage.schemes.projective.projective_space import ProjectiveSpace
    from sage.modules.free_module_element import free_module_element as vector
    from sage.rings.finite_rings.integer_mod import mod
    from __builtin__ import sum
    Fq = FiniteField(q**2, 'a')
    PG = map(tuple,ProjectiveSpace(2, Fq))
    def S(x,y):
        return sum(map(lambda j: x[j]*y[2-j]**q, xrange(3)))

    V = filter(lambda x: S(x,x)==0, PG) # the points of the unital
    v0 = V[0]
    V.remove(v0)
    if mod(q,4)==1:
        G = Graph([V,lambda y,z: not (S(v0,y)*S(y,z)*S(z,v0)).is_square()], loops=False)
    else:
        G = Graph([V,lambda y,z:     (S(v0,y)*S(y,z)*S(z,v0)).is_square()], loops=False)
    G.name("Taylor two-graph descendant SRG")
    if clique_partition:
        lines = map(lambda x: filter(lambda t: t[0]+x*t[1]==0, V),
                     filter(lambda z: z != 0, Fq))
        return (G, lines, v0)
    else:
        return G
Example #16
0
def has_blum_prime(lbound, ubound):
    """
    Determine whether or not there is a Blum prime within the specified closed
    interval.

    INPUT:

    - ``lbound`` -- positive integer; the lower bound on how small a
      Blum prime can be. The lower bound must be distinct from the upper
      bound.

    - ``ubound`` -- positive integer; the upper bound on how large a
      Blum prime can be. The lower bound must be distinct from the upper
      bound.

    OUTPUT:

    - ``True`` if there is a Blum prime ``p`` such that
      ``lbound <= p <= ubound``. ``False`` otherwise.

    ALGORITHM:

    Let `L` and `U` be distinct positive integers. Let `P` be the set of all
    odd primes `p` such that `L \leq p \leq U`. Our main focus is on Blum
    primes, i.e. odd primes that are congruent to 3 modulo 4, so we assume
    that the lower bound `L > 2`. The closed interval `[L, U]` has a Blum
    prime if and only if the set `P` has a Blum prime.

    EXAMPLES:

    Testing for the presence of Blum primes within some closed intervals.
    The interval `[4, 100]` has a Blum prime, the smallest such prime being
    7. The interval `[24, 28]` has no primes, hence no Blum primes. ::

        sage: from sage.crypto.util import has_blum_prime
        sage: from sage.crypto.util import is_blum_prime
        sage: has_blum_prime(4, 100)
        True
        sage: for n in range(4, 100):
        ....:     if is_blum_prime(n):
        ....:         print(n)
        ....:         break
        7
        sage: has_blum_prime(24, 28)
        False

    TESTS:

    Both the lower and upper bounds must be greater than 2::

        sage: from sage.crypto.util import has_blum_prime
        sage: has_blum_prime(2, 3)
        Traceback (most recent call last):
        ...
        ValueError: Both the lower and upper bounds must be > 2.
        sage: has_blum_prime(3, 2)
        Traceback (most recent call last):
        ...
        ValueError: Both the lower and upper bounds must be > 2.
        sage: has_blum_prime(2, 2)
        Traceback (most recent call last):
        ...
        ValueError: Both the lower and upper bounds must be > 2.

    The lower and upper bounds must be distinct from each other::

        sage: has_blum_prime(3, 3)
        Traceback (most recent call last):
        ...
        ValueError: The lower and upper bounds must be distinct.

    The lower bound must be less than the upper bound::

        sage: has_blum_prime(4, 3)
        Traceback (most recent call last):
        ...
        ValueError: The lower bound must be less than the upper bound.
    """
    # sanity checks
    if (lbound < 3) or (ubound < 3):
        raise ValueError("Both the lower and upper bounds must be > 2.")
    if lbound == ubound:
        raise ValueError("The lower and upper bounds must be distinct.")
    if lbound > ubound:
        raise ValueError("The lower bound must be less than the upper bound.")
    # now test for presence of a Blum prime
    for p in primes(lbound, ubound + 1):
        if mod(p, 4).lift() == 3:
            return True
    return False
Example #17
0
def _normalize(G, normal_odd=True):
    r"""
    Return the transformation to sums of forms of types `U`, `V` and `W`.

    Part of the algorithm :meth:`p_adic_normal_form`.

    INPUT:

    - ``G`` -- a symmetric matrix over `\ZZ_p` in jordan form --
      the output of :meth:`p_adic_normal_form` or :meth:`_jordan_2_adic`
    - ``normal_odd`` -- bool (default: True) if true and `p` is odd,
      compute a normal form.

    OUTPUT:

    - ``(D, B)`` -- a pair of matrices such that ``D=B*G*B.T``
      is a sum of forms of types `U`, `V` and `W` for `p=2` or
      diagonal with diagonal entries equal `1` or `u`
      where `u` is the smallest non-square modulo the odd prime `p`.

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _normalize
        sage: R = Zp(3, prec = 5, type = 'fixed-mod', print_mode='series', show_prec=False)
        sage: G = matrix.diagonal(R, [1,7,3,3*5,3,9,-9,27*13])
        sage: D, B =_normalize(G)
        sage: D
        [    1     0     0     0     0     0     0     0]
        [    0     1     0     0     0     0     0     0]
        [    0     0     3     0     0     0     0     0]
        [    0     0     0     3     0     0     0     0]
        [    0     0     0     0   2*3     0     0     0]
        [    0     0     0     0     0   3^2     0     0]
        [    0     0     0     0     0     0 2*3^2     0]
        [    0     0     0     0     0     0     0   3^3]
    """
    R = G.base_ring()
    D = copy(G)
    p = R.prime()
    n = G.ncols()
    B = copy(G.parent().identity_matrix())
    if p != 2:
        # squareclasses 1, v
        v = _min_nonsquare(p)
        v = R(v)
        non_squares = []
        val = 0
        for i in range(n):
            if D[i, i].valuation() > val:
                # a new block starts
                val = D[i, i].valuation()
                if normal_odd and len(non_squares) != 0:
                    # move the non-square to
                    # the last entry of the previous block
                    j = non_squares.pop()
                    B.swap_rows(j, i - 1)
            d = D[i, i].unit_part()
            if d.is_square():
                D[i, i] = 1
                B[i, :] *= d.inverse_of_unit().sqrt()
            else:
                D[i, i] = v
                B[i, :] *= (v * d.inverse_of_unit()).sqrt()
                if normal_odd and len(non_squares) != 0:
                    # we combine two non-squares to get
                    # the 2 x 2 identity matrix
                    j = non_squares.pop()
                    trafo = _normalize_odd_2x2(D[[i, j], [i, j]])
                    B[[i, j], :] = trafo * B[[i, j], :]
                    D[i, i] = 1
                    D[j, j] = 1
                else:
                    non_squares.append(i)
        if normal_odd and len(non_squares) != 0:
            j = non_squares.pop()
            B.swap_rows(j, n - 1)
    else:
        # squareclasses 1,3,5,7 modulo 8
        for i in range(n):
            d = D[i, i].unit_part()
            if d != 0:
                v = R(mod(d, 8))
                B[i, :] *= (v * d.inverse_of_unit()).sqrt()
        D = B * G * B.T
        for i in range(n - 1):
            if D[i, i + 1] != 0:  # there is a 2 x 2 block here
                block = D[i:i + 2, i:i + 2]
                trafo = _normalize_2x2(block)
                B[i:i + 2, :] = trafo * B[i:i + 2, :]
    D = B * G * B.T
    return D, B
Example #18
0
def _relations(G, n):
    r"""
    Return relations of `2`-adic quadratic forms.

    See [MirMor2009]_ IV Prop. 3.2. This function is for internal use only.

    INPUT:

    - ``n`` -- an integer between 1 and 10 -- the number of the relation
    - ``G`` -- a block diagonal matrix consisting of blocks of types `U, V, W`
      the left side of the relation. If ``G`` does not match `n` then the
      results are unpredictable.

    OUTPUT:

    - square matrix ``B`` such that ``B * G * B.T`` is the right side of the
      relation which consists of blocks of types `U`, `V`, `W` again

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _relations
        sage: R = Zp(2, type = 'fixed-mod',print_mode='terse', show_prec=False)
        sage: U = Matrix(R,2,[0,1,1,0])
        sage: V = Matrix(R,2,[2,1,1,2])
        sage: W1 = Matrix(R,1,[1])
        sage: W3 = Matrix(R,1,[3])
        sage: W5 = Matrix(R,1,[5])
        sage: W7 = Matrix(R,1,[7])
        sage: G = Matrix.block_diagonal(W1,W1)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [5 0]
        [0 5]
        sage: G = Matrix.block_diagonal(W1,W3)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [5 0]
        [0 7]
        sage: G = Matrix.block_diagonal(W1,W5)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [5 0]
        [0 1]
        sage: G = Matrix.block_diagonal(W1,W7)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [5 0]
        [0 3]
        sage: G = Matrix.block_diagonal(W3,W3)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [7 0]
        [0 7]
        sage: G = Matrix.block_diagonal(W3,W5)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [7 0]
        [0 1]
        sage: G = Matrix.block_diagonal(W3,W7)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [7 0]
        [0 3]
        sage: G = Matrix.block_diagonal(W5,W5)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [1 0]
        [0 1]
        sage: G = Matrix.block_diagonal(W5,W7)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [1 0]
        [0 3]
        sage: G = Matrix.block_diagonal(W7,W7)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [3 0]
        [0 3]
        sage: G = Matrix.block_diagonal([V,V])
        sage: b = _relations(G,3)
        sage: b * G * b.T
        [0 1 0 0]
        [1 0 0 0]
        [0 0 0 1]
        [0 0 1 0]
        sage: G = Matrix.block_diagonal([V,W1,W1])
        sage: b = _relations(G,5)
        sage: b * G * b.T
        [0 1 0 0]
        [1 0 0 0]
        [0 0 7 0]
        [0 0 0 3]
        sage: G = Matrix.block_diagonal([V,W1,W5])
        sage: b = _relations(G,5)
        sage: b * G * b.T
        [0 1 0 0]
        [1 0 0 0]
        [0 0 3 0]
        [0 0 0 3]
        sage: G = Matrix.block_diagonal([V,W3,W7])
        sage: b = _relations(G,5)
        sage: b * G * b.T
        [0 1 0 0]
        [1 0 0 0]
        [0 0 5 0]
        [0 0 0 5]
        sage: G = Matrix.block_diagonal([W1,2*W1])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [3 0]
        [0 6]
        sage: G = Matrix.block_diagonal([W1,2*W3])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [ 7  0]
        [ 0 10]
        sage: G = Matrix.block_diagonal([W1,2*W5])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [ 3  0]
        [ 0 14]
        sage: G = Matrix.block_diagonal([W1,2*W7])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [7 0]
        [0 2]
        sage: G = Matrix.block_diagonal([W3,2*W5])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [5 0]
        [0 6]
        sage: G = Matrix.block_diagonal([W3,2*W3])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [1 0]
        [0 2]
        sage: G = Matrix.block_diagonal([2*W5,4*W7])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [6 0]
        [0 4]
        sage: G = Matrix.block_diagonal([W3,2*V])
        sage: b = _relations(G,7)
        sage: b * G * b.T
        [7 0 0]
        [0 0 2]
        [0 2 0]
        sage: G = Matrix.block_diagonal([W7,2*V])
        sage: b = _relations(G,7)
        sage: b * G * b.T
        [3 0 0]
        [0 0 2]
        [0 2 0]
        sage: G = Matrix.block_diagonal([U,2*W1])
        sage: b = _relations(G,8)
        sage: b * G * b.T
        [ 2  1  0]
        [ 1  2  0]
        [ 0  0 10]
        sage: G = Matrix.block_diagonal([U,2*W5])
        sage: b = _relations(G,8)
        sage: b * G * b.T
        [2 1 0]
        [1 2 0]
        [0 0 2]
        sage: G = Matrix.block_diagonal([V,2*W1])
        sage: b = _relations(G,8)
        sage: b * G * b.T
        [ 0  1  0]
        [ 1  0  0]
        [ 0  0 10]
        sage: G = Matrix.block_diagonal([V,2*W7])
        sage: b = _relations(G,8)
        sage: b * G * b.T
        [0 1 0]
        [1 0 0]
        [0 0 6]
        sage: G = Matrix.block_diagonal([W1,W5,2*W5])
        sage: b = _relations(G,9)
        sage: b * G * b.T
        [3 0 0]
        [0 3 0]
        [0 0 2]
        sage: G = Matrix.block_diagonal([W3,W3,2*W5])
        sage: b = _relations(G,9)
        sage: b * G * b.T
        [5 0 0]
        [0 1 0]
        [0 0 2]
        sage: G = Matrix.block_diagonal([W3,W3,2*W1])
        sage: b = _relations(G,9)
        sage: b * G * b.T
        [ 5  0  0]
        [ 0  1  0]
        [ 0  0 10]
        sage: G = Matrix.block_diagonal([W3,4*W1])
        sage: b = _relations(G,10)
        sage: b * G * b.T
        [ 7  0]
        [ 0 20]
        sage: G = Matrix.block_diagonal([W5,4*W5])
        sage: b = _relations(G,10)
        sage: b * G * b.T
        [1 0]
        [0 4]
    """
    R = G.base_ring()
    if n == 1:
        e1 = G[0, 0].unit_part()
        e2 = G[1, 1].unit_part()
        B = Matrix(R, 2, [1, 2, 2 * e2, -e1])
    if n == 2:
        e1 = G[0, 0].unit_part()
        e2 = G[1, 1].unit_part()
        e3 = G[2, 2].unit_part()
        B = Matrix(R, 3, [1, 1, 1, e2, -e1, 0, e3, 0, -e1])
    if n == 3:
        B = Matrix(R, 4, [1, 1, 1, 0, 1, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, -1])
    if n == 4:
        raise NotImplementedError("relation 4 is not needed")
    if n == 5:
        e1 = G[2, 2].unit_part()
        e2 = G[3, 3].unit_part()
        if mod(e1, 4) != mod(e2, 4):
            raise ValueError("W is of the wrong type for relation 5")
        B = Matrix(R, 4, [
            1, 0, 1, 1, 0, 1, 1, 1, -e2, -e2, 0, 3, -e1, -e1, 2 * e2 + 3,
            -2 * e1
        ])
    if n == 6:
        if G[0, 0].valuation() + 1 != G[1, 1].valuation():
            raise ValueError("wrong scales for relation 6")
        e1 = G[0, 0].unit_part()
        e2 = G[1, 1].unit_part()
        B = Matrix(R, 2, [1, 1, -2 * e2, e1])
    if n == 7:
        e = G[0, 0].unit_part()
        B = Matrix(R, 3, [-3, e**2, e**2, 2 * e, 1, 0, 2 * e, 0, 1])
    if n == 8:
        e = G[2, 2].unit_part()
        if G[0, 0] == 0:
            B = Matrix(R, 3, [e, 0, -1, 0, e, -1, 2, 2, 1])
        else:
            B = Matrix(R, 3, [1, 0, 1, 0, 1, 1, 2 * e, 2 * e, -3])
    if n == 9:
        e1 = G[0, 0].unit_part()
        e2 = G[1, 1].unit_part()
        e3 = G[2, 2].unit_part()
        B = Matrix(R, 3, [
            1, 0, 1, 2 * e3, 1, -e1, -2 * e2 * e3,
            2 * e1**2 * e3 + 4 * e1 * e3**2, e1 * e2
        ])
    if n == 10:
        e1 = G[0, 0].unit_part()
        e2 = G[1, 1].unit_part()
        B = Matrix(R, 2, [1, 1, -4 * e2, e1])
    D, B1 = _normalize(B * G * B.T)
    return B1 * B
Example #19
0
def random_blum_prime(lbound, ubound, ntries=100):
    r"""
    A random Blum prime within the specified bounds.

    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}`.

    INPUT:

    - ``lbound`` -- positive integer; the lower bound on how small a
      random Blum prime `p` can be. So we have
      ``0 < lbound <= p <= ubound``. The lower bound must be distinct from
      the upper bound.

    - ``ubound`` -- positive integer; the upper bound on how large a
      random Blum prime `p` can be. So we have
      ``0 < lbound <= p <= ubound``. The lower bound must be distinct
      from the upper bound.

    - ``ntries`` -- (default: ``100``) the number of attempts to generate
      a random Blum prime. If ``ntries`` is a positive integer, then
      perform that many attempts at generating a random Blum prime. This
      might or might not result in a Blum prime.

    OUTPUT:

    - A random Blum prime within the specified lower and upper bounds.

    .. 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 Blum primes within the specified bounds.

    EXAMPLES:

    Choose a random prime and check that it is a Blum prime::

        sage: from sage.crypto.util import random_blum_prime
        sage: p = random_blum_prime(10**4, 10**5)
        sage: is_prime(p)
        True
        sage: mod(p, 4) == 3
        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.util import random_blum_prime
        sage: random_blum_prime(24, 30, ntries=10)
        Traceback (most recent call last):
        ...
        ValueError: No Blum primes within the specified closed interval.
        sage: random_blum_prime(24, 28)
        Traceback (most recent call last):
        ...
        ValueError: No Blum primes within the specified closed interval.
    """
    # sanity check
    if not has_blum_prime(lbound, ubound):
        raise ValueError("No Blum primes within the specified closed interval.")
    # Now we know that there is a Blum prime within the closed interval
    # [lbound, ubound]. Pick one such prime at random.
    p = random_prime(ubound, lbound=lbound, proof=True)
    n = 1
    while mod(p, 4) != 3:
        p = random_prime(ubound, lbound=lbound, proof=True)
        n += 1
        if n > ntries:
            raise ValueError("Maximum number of attempts exceeded.")
    return p
Example #20
0
def bin_to_ascii(B):
    r"""
    Return the ASCII representation of the binary string ``B``.

    INPUT:

    - ``B`` -- a non-empty binary string or a non-empty list of bits. The
      number of bits in ``B`` must be a multiple of 8.

    OUTPUT:

    - The ASCII string corresponding to ``B``.

    ALGORITHM:

    Consider a block of bits `B = b_0 b_1 \cdots b_{n-1}` where each
    sub-block `b_i` is a binary string of length 8. Then the total number
    of bits is a multiple of 8 and is given by `8n`. Let `c_i` be the
    integer representation of `b_i`. We can consider `c_i` as the integer
    representation of an ASCII character. Then the ASCII representation
    `A` of `B` is `A = a_0 a_1 \cdots a_{n-1}`.

    EXAMPLES:

    Convert some ASCII strings to their binary representations and recover
    the ASCII strings from the binary representations::

        sage: from sage.crypto.util import ascii_to_bin
        sage: from sage.crypto.util import bin_to_ascii
        sage: A = "Abc"
        sage: B = ascii_to_bin(A); B
        010000010110001001100011
        sage: bin_to_ascii(B)
        'Abc'
        sage: bin_to_ascii(B) == A
        True

    ::

        sage: A = "123 \" #"
        sage: B = ascii_to_bin(A); B
        00110001001100100011001100100000001000100010000000100011
        sage: bin_to_ascii(B)
        '123 " #'
        sage: bin_to_ascii(B) == A
        True

    This function also accepts strings and lists of bits::

        sage: from sage.crypto.util import bin_to_ascii
        sage: bin_to_ascii("010000010110001001100011")
        'Abc'
        sage: bin_to_ascii([0, 1, 0, 0, 0, 0, 0, 1])
        'A'

    TESTS:

    The number of bits in ``B`` must be non-empty and a multiple of 8::

        sage: from sage.crypto.util import bin_to_ascii
        sage: bin_to_ascii("")
        Traceback (most recent call last):
        ...
        ValueError: B must be a non-empty binary string.
        sage: bin_to_ascii([])
        Traceback (most recent call last):
        ...
        ValueError: B must be a non-empty binary string.
        sage: bin_to_ascii(" ")
        Traceback (most recent call last):
        ...
        ValueError: The number of bits in B must be a multiple of 8.
        sage: bin_to_ascii("101")
        Traceback (most recent call last):
        ...
        ValueError: The number of bits in B must be a multiple of 8.
        sage: bin_to_ascii([1, 0, 1])
        Traceback (most recent call last):
        ...
        ValueError: The number of bits in B must be a multiple of 8.
    """
    # sanity checks
    n = len(B)
    if n == 0:
        raise ValueError("B must be a non-empty binary string.")
    if mod(n, 8) != 0:
        raise ValueError("The number of bits in B must be a multiple of 8.")
    # perform conversion to ASCII string
    b = [int(str(x)) for x in list(B)]
    A = []
    # the number of 8-bit blocks
    k = n // 8
    for i in range(k):
        # Convert from 8-bit string to ASCII integer. Then convert the
        # ASCII integer to the corresponding ASCII character.
        A.append(chr(ascii_integer(b[8*i: 8*(i+1)])))
    return "".join(A)
Example #21
0
def _two_adic_normal_forms(G, partial=False):
    r"""
    Return the 2-adic normal form of a symmetric matrix.

    INPUT:

    - ``G`` -- block diagonal matrix with blocks of type `U`, `V`, `W`
    - ``partial`` -- bool (defaul: ``False``)

    OUTPUT:

    - ``D``, ``B`` -- such that ``D = B * G * B.T``

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _two_adic_normal_forms
        sage: R = Zp(2, type = 'fixed-mod', print_mode='terse', show_prec=False)
        sage: U = Matrix(R,2,[0,1,1,0])
        sage: V = Matrix(R,2,[2,1,1,2])
        sage: W1 = Matrix(R,1,[1])
        sage: W3 = Matrix(R,1,[3])
        sage: W5 = Matrix(R,1,[5])
        sage: W7 = Matrix(R,1,[7])
        sage: G = Matrix.block_diagonal([2*W1,2*W1,4*V])
        sage: B = _two_adic_normal_forms(G)[1]
        sage: B * G * B.T
        [ 2  0  0  0]
        [ 0 10  0  0]
        [ 0  0  0  4]
        [ 0  0  4  0]
        sage: G = Matrix.block_diagonal([W1,2*V,2*W3,2*W5])
        sage: B = _two_adic_normal_forms(G)[1]
        sage: B * G * B.T
        [3 0 0 0 0]
        [0 0 2 0 0]
        [0 2 0 0 0]
        [0 0 0 2 0]
        [0 0 0 0 2]
        sage: G = Matrix.block_diagonal([U,2*V,2*W3,2*W5])
        sage: B = _two_adic_normal_forms(G)[1]
        sage: B * G * B.T
        [2 1 0 0 0 0]
        [1 2 0 0 0 0]
        [0 0 4 2 0 0]
        [0 0 2 4 0 0]
        [0 0 0 0 2 0]
        [0 0 0 0 0 6]
    """
    B = copy(G.parent().identity_matrix())
    h, scales = _get_homogeneous_block_indices(G)
    h.append(B.ncols())
    # UVlist[k] is a list of indices of the block of scale p^k.
    # It contains the indices of the part of types U or V.
    # So it may be empty.
    UVlist = [[],[]]       # empty lists are appended to avoid special cases.
    # same as UVlist but contains the indices of the part of type W
    Wlist = [[],[]]
    # homogeneous normal form for each part
    for k in range(scales[-1] - scales[0]+1):
        if k+scales[0] in scales:
            i = scales.index(k + scales[0])
            Gk = G[h[i]:h[i+1], h[i]:h[i+1]]
            Dk, Bk, wk = _partial_normal_form_of_block(Gk)
            B[h[i]:h[i+1],:] = Bk * B[h[i]:h[i+1], :]
            if not partial:
                Dk, B1k = _homogeneous_normal_form(Dk, wk)
                B[h[i]:h[i+1],:] = B1k * B[h[i]:h[i+1], :]
            UVlist.append(range(h[i], h[i+1] - wk))
            Wlist.append(range(h[i+1]-wk, h[i+1]))
        else:
            UVlist.append([])
            Wlist.append([])
    D = B * G * B.T
    if partial:
        return D, B
    # use relations descending in k
    # we never leave partial normal form
    # but the homogneneous normal form may be destroyed
    # it is restored at the end.
    for k in range(len(UVlist)-1,2,-1):
        # setup notation
        W = Wlist[k]
        Wm = Wlist[k-1]
        Wmm = Wlist[k-2]
        UV = UVlist[k]
        UVm = UVlist[k-1]
        V = UVlist[k][-2:]
        if len(V)!=0 and D[V[0], V[0]]==0:
            V = []    # it is U not V
        # condition b)
        if len(Wm) != 0:
            if len(V)==2:
                R = Wm[:1] + V
                B[R,:] = _relations(D[R,R],7) * B[R,:]
                V = []
                D = B * G * B.T
            E = {3,7}
            for w in W:
                if D[w,w].unit_part() in E:
                    R = Wm[:1] + [w]
                    B[R,:] = _relations(D[R,R],6) * B[R,:]
                    D = B * G * B.T
        # condition c)
        # We want type a or W = []
        # modify D[w,w] to go from type b to type a
        x = [len(V)] + [ZZ(mod(w.unit_part(),8)) for w in D[W,W].diagonal()]
        x.sort()
      # a = [[0,1], [2,3], [2,5], [0,7], [0,1,1], [1,2,3], [0,7,7], [0,1,7]]
        b = [[0,5], [2,7], [1,2], [0,3], [0,1,5], [1,2,7], [0,3,7], [0,1,3]]
        if x in b:
            w = W[-1]
            if x == [3,7]:
                w = W[0]
            if len(UVm) > 0:
                R = UVm[-2:] + [w]
                B[R,:] = _relations(D[R,R],8) * B[R,:]
            elif len(Wmm) > 0:
                R = Wmm[:1] + [w]
                B[R,:] = _relations(D[R,R],10) * B[R,:]
            elif len(Wm) == 2:
                e0 = D[Wm,Wm][0,0].unit_part()
                e1 = D[Wm,Wm][1,1].unit_part()
                if mod(e1-e0,4) == 0:
                    R = Wm + [w]
                    B[R,:] = _relations(D[R,R],9) * B[R,:]
        D = B * G * B.T
        # condition a) - stay in homogneneous normal form
        R = UV + W
        Dk = D[R,R]
        Bk = _homogeneous_normal_form(Dk, len(W))[1]
        B[R,:] = Bk * B[R,:]
        D = B * G * B.T
        # we need to restore the homogeneous normal form of  k-1
        if len(Wm)>0:
            R = UVm + Wm
            Dkm = D[R,R]
            Bkm = _homogeneous_normal_form(Dkm, len(Wm))[1]
            B[R,:] = Bkm * B[R,:]
            D = B * G * B.T
    return D, B
Example #22
0
def _relations(G,n):
    r"""
    Return relations of `2`-adic quadratic forms.

    See [MirMor2009]_ IV Prop. 3.2. This function is for internal use only.

    INPUT:

    - ``n`` -- an integer between 1 and 10 -- the number of the relation
    - ``G`` -- a block diagonal matrix consisting of blocks of types `U, V, W`
      the left side of the relation. If ``G`` does not match `n` then the
      results are unpredictable.

    OUTPUT:

    - square matrix ``B`` such that ``B * G * B.T`` is the right side of the
      relation which consits of blocks of types `U`, `V`, `W` again

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _relations
        sage: R = Zp(2, type = 'fixed-mod',print_mode='terse', show_prec=False)
        sage: U = Matrix(R,2,[0,1,1,0])
        sage: V = Matrix(R,2,[2,1,1,2])
        sage: W1 = Matrix(R,1,[1])
        sage: W3 = Matrix(R,1,[3])
        sage: W5 = Matrix(R,1,[5])
        sage: W7 = Matrix(R,1,[7])
        sage: G = Matrix.block_diagonal(W1,W1)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [5 0]
        [0 5]
        sage: G = Matrix.block_diagonal(W1,W3)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [5 0]
        [0 7]
        sage: G = Matrix.block_diagonal(W1,W5)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [5 0]
        [0 1]
        sage: G = Matrix.block_diagonal(W1,W7)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [5 0]
        [0 3]
        sage: G = Matrix.block_diagonal(W3,W3)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [7 0]
        [0 7]
        sage: G = Matrix.block_diagonal(W3,W5)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [7 0]
        [0 1]
        sage: G = Matrix.block_diagonal(W3,W7)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [7 0]
        [0 3]
        sage: G = Matrix.block_diagonal(W5,W5)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [1 0]
        [0 1]
        sage: G = Matrix.block_diagonal(W5,W7)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [1 0]
        [0 3]
        sage: G = Matrix.block_diagonal(W7,W7)
        sage: b = _relations(G,1)
        sage: b * G * b.T
        [3 0]
        [0 3]
        sage: G = Matrix.block_diagonal([V,V])
        sage: b = _relations(G,3)
        sage: b * G * b.T
        [0 1 0 0]
        [1 0 0 0]
        [0 0 0 1]
        [0 0 1 0]
        sage: G = Matrix.block_diagonal([V,W1,W1])
        sage: b = _relations(G,5)
        sage: b * G * b.T
        [0 1 0 0]
        [1 0 0 0]
        [0 0 7 0]
        [0 0 0 3]
        sage: G = Matrix.block_diagonal([V,W1,W5])
        sage: b = _relations(G,5)
        sage: b * G * b.T
        [0 1 0 0]
        [1 0 0 0]
        [0 0 3 0]
        [0 0 0 3]
        sage: G = Matrix.block_diagonal([V,W3,W7])
        sage: b = _relations(G,5)
        sage: b * G * b.T
        [0 1 0 0]
        [1 0 0 0]
        [0 0 5 0]
        [0 0 0 5]
        sage: G = Matrix.block_diagonal([W1,2*W1])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [3 0]
        [0 6]
        sage: G = Matrix.block_diagonal([W1,2*W3])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [ 7  0]
        [ 0 10]
        sage: G = Matrix.block_diagonal([W1,2*W5])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [ 3  0]
        [ 0 14]
        sage: G = Matrix.block_diagonal([W1,2*W7])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [7 0]
        [0 2]
        sage: G = Matrix.block_diagonal([W3,2*W5])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [5 0]
        [0 6]
        sage: G = Matrix.block_diagonal([W3,2*W3])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [1 0]
        [0 2]
        sage: G = Matrix.block_diagonal([2*W5,4*W7])
        sage: b = _relations(G,6)
        sage: b * G * b.T
        [6 0]
        [0 4]
        sage: G = Matrix.block_diagonal([W3,2*V])
        sage: b = _relations(G,7)
        sage: b * G * b.T
        [7 0 0]
        [0 0 2]
        [0 2 0]
        sage: G = Matrix.block_diagonal([W7,2*V])
        sage: b = _relations(G,7)
        sage: b * G * b.T
        [3 0 0]
        [0 0 2]
        [0 2 0]
        sage: G = Matrix.block_diagonal([U,2*W1])
        sage: b = _relations(G,8)
        sage: b * G * b.T
        [ 2  1  0]
        [ 1  2  0]
        [ 0  0 10]
        sage: G = Matrix.block_diagonal([U,2*W5])
        sage: b = _relations(G,8)
        sage: b * G * b.T
        [2 1 0]
        [1 2 0]
        [0 0 2]
        sage: G = Matrix.block_diagonal([V,2*W1])
        sage: b = _relations(G,8)
        sage: b * G * b.T
        [ 0  1  0]
        [ 1  0  0]
        [ 0  0 10]
        sage: G = Matrix.block_diagonal([V,2*W7])
        sage: b = _relations(G,8)
        sage: b * G * b.T
        [0 1 0]
        [1 0 0]
        [0 0 6]
        sage: G = Matrix.block_diagonal([W1,W5,2*W5])
        sage: b = _relations(G,9)
        sage: b * G * b.T
        [3 0 0]
        [0 3 0]
        [0 0 2]
        sage: G = Matrix.block_diagonal([W3,W3,2*W5])
        sage: b = _relations(G,9)
        sage: b * G * b.T
        [5 0 0]
        [0 1 0]
        [0 0 2]
        sage: G = Matrix.block_diagonal([W3,W3,2*W1])
        sage: b = _relations(G,9)
        sage: b * G * b.T
        [ 5  0  0]
        [ 0  1  0]
        [ 0  0 10]
        sage: G = Matrix.block_diagonal([W3,4*W1])
        sage: b = _relations(G,10)
        sage: b * G * b.T
        [ 7  0]
        [ 0 20]
        sage: G = Matrix.block_diagonal([W5,4*W5])
        sage: b = _relations(G,10)
        sage: b * G * b.T
        [1 0]
        [0 4]
    """
    R = G.base_ring()
    if n == 1:
        e1 = G[0,0].unit_part()
        e2 = G[1,1].unit_part()
        B = Matrix(R,2,[1,2,2*e2,-e1])
    if n == 2:
        e1 = G[0,0].unit_part()
        e2 = G[1,1].unit_part()
        e3 = G[2,2].unit_part()
        s1 = e1 + e2 + e3
        s2 = e1*e2 + e1*e3 + e2*e3
        s3 = e1*e2*e3
        B = Matrix(R,3,[1,1,1,e2,-e1,0,e3,0,-e1])
    if n == 3:
        B = Matrix(R,4,[1,1,1,0, 1,1,0,1, 1,0,-1,-1, 0,1,-1,-1])
    if n == 4:
        raise NotImplementedError("relation 4 is not needed")
    if n == 5:
        e1 = G[2,2].unit_part()
        e2 = G[3,3].unit_part()
        if mod(e1,4) != mod(e2,4):
            raise ValueError("W is of the wrong type for relation 5")
        B = Matrix(R,4,[  1,   0,        1,     1,
                          0,   1,        1,     1,
                        -e2, -e2,        0,     3,
                        -e1, -e1, 2*e2 + 3, -2*e1])
    if n == 6:
        if G[0,0].valuation()+1 != G[1,1].valuation():
            raise ValueError("wrong scales for relation 6")
        e1 = G[0,0].unit_part()
        e2 = G[1,1].unit_part()
        B = Matrix(R,2,[1,1,-2*e2,e1])
    if n == 7:
        e = G[0,0].unit_part()
        B = Matrix(R,3,[-3, e**2, e**2, 2*e, 1, 0, 2*e, 0, 1])
    if n == 8:
        e = G[2,2].unit_part()
        if G[0,0]==0:
            B = Matrix(R,3,[e, 0, -1,
                            0, e, -1,
                            2, 2,  1])
        else:
            B = Matrix(R,3,[  1,   0,   1,
                              0,   1,   1,
                            2*e, 2*e, - 3])
    if n == 9:
        e1 = G[0,0].unit_part()
        e2 = G[1,1].unit_part()
        e3 = G[2,2].unit_part()
        B = Matrix(R,3,[1, 0, 1,
                        2*e3, 1,
                        -e1, -2*e2*e3, 2*e1**2*e3 + 4*e1*e3**2, e1*e2])
    if n == 10:
        e1 = G[0,0].unit_part()
        e2 = G[1,1].unit_part()
        B = Matrix(R,2,[1,1,-4*e2,e1])
    D, B1 = _normalize(B*G*B.T)
    return B1*B
Example #23
0
def _partial_normal_form_of_block(G):
    r"""
    Return the partial normal form of the homogeneous block ``G``.

    For internal use in :meth:`_two_adic_normal_forms`.

    INPUT:

    - ``G`` -- a modular symmetric matrix over the `2`-adic integers

    OUTPUT:

    - ``D, B, w`` -- with ``B`` a transformation matrix such that
      ``B * G * B.T`` is in partial normal form
      and `w = 0, 1, 2` is the size of the part consisting of forms of type W

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _partial_normal_form_of_block
        sage: R = Zp(2,prec=4, type = 'fixed-mod',print_mode='terse', show_prec=False)
        sage: U = Matrix(R, 2, [0,1,1,0])
        sage: V = Matrix(R, 2, [2,1,1,2])
        sage: W1 = Matrix(R, 1, [1])
        sage: W3 = Matrix(R, 1, [3])
        sage: W5 = Matrix(R, 1, [5])
        sage: W7 = Matrix(R, 1, [7])
        sage: G = Matrix.block_diagonal([W1, U, V, W5, V, W3, V, W7])
        sage: B = _partial_normal_form_of_block(G)[1]
        sage: B * G * B.T
        [0 1 0 0 0 0 0 0 0 0 0 0]
        [1 0 0 0 0 0 0 0 0 0 0 0]
        [0 0 0 1 0 0 0 0 0 0 0 0]
        [0 0 1 0 0 0 0 0 0 0 0 0]
        [0 0 0 0 0 1 0 0 0 0 0 0]
        [0 0 0 0 1 0 0 0 0 0 0 0]
        [0 0 0 0 0 0 0 1 0 0 0 0]
        [0 0 0 0 0 0 1 0 0 0 0 0]
        [0 0 0 0 0 0 0 0 2 1 0 0]
        [0 0 0 0 0 0 0 0 1 2 0 0]
        [0 0 0 0 0 0 0 0 0 0 1 0]
        [0 0 0 0 0 0 0 0 0 0 0 7]
        sage: G = Matrix.block_diagonal([W1, U, V, W1, V, W1, V, W7])
        sage: B = _partial_normal_form_of_block(G)[1]
        sage: B * G * B.T
        [0 1 0 0 0 0 0 0 0 0 0 0]
        [1 0 0 0 0 0 0 0 0 0 0 0]
        [0 0 0 1 0 0 0 0 0 0 0 0]
        [0 0 1 0 0 0 0 0 0 0 0 0]
        [0 0 0 0 0 1 0 0 0 0 0 0]
        [0 0 0 0 1 0 0 0 0 0 0 0]
        [0 0 0 0 0 0 0 1 0 0 0 0]
        [0 0 0 0 0 0 1 0 0 0 0 0]
        [0 0 0 0 0 0 0 0 0 1 0 0]
        [0 0 0 0 0 0 0 0 1 0 0 0]
        [0 0 0 0 0 0 0 0 0 0 3 0]
        [0 0 0 0 0 0 0 0 0 0 0 7]
    """
    D = copy(G)
    R = D.base_ring()
    n = D.ncols()
    B = copy(G.parent().identity_matrix())     # the transformation matrix
    blocks = _get_small_block_indices(D)
    # collect the indices of forms of types U, V and W
    U = []
    V = []
    W = []
    for i in blocks:
        if i+1 in blocks or i==n-1:
            W.append(i)
        else:
            if D[i,i] != 0:
                V += [i,i+1]
            else:
                U += [i,i+1]
        if len(W) == 3:
            # W W W transforms to W U or W V
            T = _relations(D[W,W], 2)
            B[W,:] = _relations(D[W,W],2) * B[W,:]
            D = B * G * B.T
            if mod(D[W[1:], W[1:]].det().unit_part(), 8) == 3:
                V += W[1:]
            else:
                U += W[1:]
            W = W[:1]
        if len(V) == 4:
            B[V,:] = _relations(D[V,V],3) * B[V,:]
            U += V
            V = []
            D = B * G * B.T
    # put everything into the right order
    UVW = U + V + W
    B = B[UVW,:]
    D = B * G * B.T
    return D, B, len(W)
Example #24
0
def has_blum_prime(lbound, ubound):
    """
    Determine whether or not there is a Blum prime within the specified closed
    interval.

    INPUT:

    - ``lbound`` -- positive integer; the lower bound on how small a
      Blum prime can be. The lower bound must be distinct from the upper
      bound.

    - ``ubound`` -- positive integer; the upper bound on how large a
      Blum prime can be. The lower bound must be distinct from the upper
      bound.

    OUTPUT:

    - ``True`` if there is a Blum prime ``p`` such that
      ``lbound <= p <= ubound``. ``False`` otherwise.

    ALGORITHM:

    Let `L` and `U` be distinct positive integers. Let `P` be the set of all
    odd primes `p` such that `L \leq p \leq U`. Our main focus is on Blum
    primes, i.e. odd primes that are congruent to 3 modulo 4, so we assume
    that the lower bound `L > 2`. The closed interval `[L, U]` has a Blum
    prime if and only if the set `P` has a Blum prime.

    EXAMPLES:

    Testing for the presence of Blum primes within some closed intervals.
    The interval `[4, 100]` has a Blum prime, the smallest such prime being
    7. The interval `[24, 28]` has no primes, hence no Blum primes. ::

        sage: from sage.crypto.util import has_blum_prime
        sage: from sage.crypto.util import is_blum_prime
        sage: has_blum_prime(4, 100)
        True
        sage: for n in range(4, 100):
        ....:     if is_blum_prime(n):
        ....:         print(n)
        ....:         break
        7
        sage: has_blum_prime(24, 28)
        False

    TESTS:

    Both the lower and upper bounds must be greater than 2::

        sage: from sage.crypto.util import has_blum_prime
        sage: has_blum_prime(2, 3)
        Traceback (most recent call last):
        ...
        ValueError: Both the lower and upper bounds must be > 2.
        sage: has_blum_prime(3, 2)
        Traceback (most recent call last):
        ...
        ValueError: Both the lower and upper bounds must be > 2.
        sage: has_blum_prime(2, 2)
        Traceback (most recent call last):
        ...
        ValueError: Both the lower and upper bounds must be > 2.

    The lower and upper bounds must be distinct from each other::

        sage: has_blum_prime(3, 3)
        Traceback (most recent call last):
        ...
        ValueError: The lower and upper bounds must be distinct.

    The lower bound must be less than the upper bound::

        sage: has_blum_prime(4, 3)
        Traceback (most recent call last):
        ...
        ValueError: The lower bound must be less than the upper bound.
    """
    # sanity checks
    if (lbound < 3) or (ubound < 3):
        raise ValueError("Both the lower and upper bounds must be > 2.")
    if lbound == ubound:
        raise ValueError("The lower and upper bounds must be distinct.")
    if lbound > ubound:
        raise ValueError("The lower bound must be less than the upper bound.")
    # now test for presence of a Blum prime
    for p in primes(lbound, ubound + 1):
        if mod(p, 4).lift() == 3:
            return True
    return False
Example #25
0
def _normalize_2x2(G):
    r"""
    Normalize this indecomposable `2` by `2` block.

    INPUT:

    ``G`` - a `2` by `2` matrix over `\ZZ_p`
    with ``type = 'fixed-mod'`` of the form::

        [2a  b]
        [ b 2c] * 2^n

    with `b` of valuation 1.

    OUTPUT:

    A unimodular `2` by `2` matrix ``B`` over `\ZZ_p` with
    ``B * G * B.transpose()``
    either::

        [0 1]              [2 1]
        [1 0] * 2^n  or    [1 2] * 2^n

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _normalize_2x2
        sage: R = Zp(2, prec = 15, type = 'fixed-mod', print_mode='series', show_prec=False)
        sage: G = Matrix(R, 2, [-17*2,3,3,23*2])
        sage: B =_normalize_2x2(G)
        sage: B * G * B.T
        [2 1]
        [1 2]

        sage: G = Matrix(R,2,[-17*4,3,3,23*2])
        sage: B = _normalize_2x2(G)
        sage: B*G*B.T
        [0 1]
        [1 0]

        sage: G = 2^3 * Matrix(R, 2, [-17*2,3,3,23*2])
        sage: B = _normalize_2x2(G)
        sage: B * G * B.T
        [2^4 2^3]
        [2^3 2^4]
    """
    from sage.rings.all import PolynomialRing
    from sage.modules.free_module_element import vector
    B = copy(G.parent().identity_matrix())
    R = G.base_ring()
    P = PolynomialRing(R, 'x')
    x = P.gen()

    # The input must be an even block
    odd1 = (G[0, 0].valuation() < G[1, 0].valuation())
    odd2 = (G[1, 1].valuation() < G[1, 0].valuation())
    if  odd1 or odd2:
            raise ValueError("Not a valid 2 x 2 block.")
    scale = 2 ** G[0,1].valuation()
    D = Matrix(R, 2, 2, [d // scale for d in G.list()])
    # now D is of the form
    # [2a b ]
    # [b  2c]
    # where b has valuation 1.
    G = copy(D)

    # Make sure G[1, 1] has valuation 1.
    if D[1, 1].valuation() > D[0, 0].valuation():
        B.swap_columns(0, 1)
        D.swap_columns(0, 1)
        D.swap_rows(0, 1)
    if D[1, 1].valuation() != 1:
        # this works because
        # D[0, 0] has valuation at least 2
        B[1, :] += B[0, :]
        D = B * G * B.transpose()
    assert D[1, 1].valuation() == 1

    if mod(D.det(), 8) == 3:
        #  in this case we can transform D to
        #  2 1
        #  1 2
        # Find a point of norm 2
        # solve: 2 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0]
        pol = (D[1,1]*x**2 + 2*D[1,0]*x + D[0,0]-2) // 2
        # somehow else pari can get a hickup see `trac`:#24065
        pol = pol // pol.leading_coefficient()
        sol = pol.roots()[0][0]
        B[0, 1] = sol
        D = B * G * B.transpose()
        # make D[0, 1] = 1
        B[1, :] *= D[1, 0].inverse_of_unit()
        D = B * G * B.transpose()

        # solve: v*D*v == 2 with v = (x, -2*x+1)
        if D[1, 1] != 2:
            v = vector([x, -2*x + 1])
            pol = (v*D*v - 2) // 2
            # somehow else pari can get a hickup `trac`:#24065
            pol = pol // pol.leading_coefficient()
            sol = pol.roots()[0][0]
            B[1, :] = sol * B[0,:] + (-2*sol + 1)*B[1, :]
            D = B * G * B.transpose()
        # check the result
        assert D == Matrix(G.parent(), 2, 2, [2, 1, 1, 2]), "D1 \n %r" %D
    elif mod(D.det(), 8) == 7:
        # in this case we can transform D to
        #  0 1
        #  1 0
        # Find a point representing 0
        # solve: 0 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0]
        pol = (D[1,1]*x**2 + 2*D[1,0]*x + D[0,0])//2
        # somehow else pari can get a hickup, see  `trac`:#24065
        pol = pol // pol.leading_coefficient()
        sol = pol.roots()[0][0]
        B[0,:] += sol*B[1, :]
        D = B * G * B.transpose()
        # make the second basis vector have 0 square as well.
        B[1, :] = B[1, :] - D[1, 1]//(2*D[0, 1])*B[0,:]
        D = B * G * B.transpose()
        # rescale to get D[0,1] = 1
        B[0, :] *= D[1, 0].inverse_of_unit()
        D = B * G * B.transpose()
        # check the result
        assert D == Matrix(G.parent(), 2, 2, [0, 1, 1, 0]), "D2 \n %r" %D
    return B
Example #26
0
def random_blum_prime(lbound, ubound, ntries=100):
    r"""
    A random Blum prime within the specified bounds.

    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}`.

    INPUT:

    - ``lbound`` -- positive integer; the lower bound on how small a
      random Blum prime `p` can be. So we have
      ``0 < lbound <= p <= ubound``. The lower bound must be distinct from
      the upper bound.

    - ``ubound`` -- positive integer; the upper bound on how large a
      random Blum prime `p` can be. So we have
      ``0 < lbound <= p <= ubound``. The lower bound must be distinct
      from the upper bound.

    - ``ntries`` -- (default: ``100``) the number of attempts to generate
      a random Blum prime. If ``ntries`` is a positive integer, then
      perform that many attempts at generating a random Blum prime. This
      might or might not result in a Blum prime.

    OUTPUT:

    - A random Blum prime within the specified lower and upper bounds.

    .. 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 Blum primes within the specified bounds.

    EXAMPLES:

    Choose a random prime and check that it is a Blum prime::

        sage: from sage.crypto.util import random_blum_prime
        sage: p = random_blum_prime(10**4, 10**5)
        sage: is_prime(p)
        True
        sage: mod(p, 4) == 3
        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.util import random_blum_prime
        sage: random_blum_prime(24, 30, ntries=10)
        Traceback (most recent call last):
        ...
        ValueError: No Blum primes within the specified closed interval.
        sage: random_blum_prime(24, 28)
        Traceback (most recent call last):
        ...
        ValueError: No Blum primes within the specified closed interval.
    """
    # sanity check
    if not has_blum_prime(lbound, ubound):
        raise ValueError("No Blum primes within the specified closed interval.")
    # Now we know that there is a Blum prime within the closed interval
    # [lbound, ubound]. Pick one such prime at random.
    p = random_prime(ubound, lbound=lbound, proof=True)
    n = 1
    while mod(p, 4) != 3:
        p = random_prime(ubound, lbound=lbound, proof=True)
        n += 1
        if n > ntries:
            raise ValueError("Maximum number of attempts exceeded.")
    return p
Example #27
0
    def decrypt(self, C, K):
        r"""
        Apply the Blum-Goldwasser scheme to decrypt the ciphertext ``C``
        using the private key ``K``.

        INPUT:

        - ``C`` -- a ciphertext resulting from encrypting a plaintext using
          the Blum-Goldwasser encryption algorithm. The ciphertext `C` must
          be of the form `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Each `c_i`
          is a sub-block of binary string and `x_{t+1}` is the result of the
          `t+1`-th iteration of the Blum-Blum-Shub algorithm.

        - ``K`` -- a private key `(p, q, a, b)` where `p` and `q` are
          distinct Blum primes and `\gcd(p, q) = ap + bq = 1`.

        OUTPUT:

        - The plaintext resulting from decrypting the ciphertext ``C`` using
          the Blum-Goldwasser decryption algorithm.

        ALGORITHM:

        The Blum-Goldwasser decryption algorithm is described in Algorithm
        8.56, page 309 of [MenezesEtAl1996]_. The algorithm works as follows:

        #. Let `C` be the ciphertext `C = (c_1, c_2, \dots, c_t, x_{t+1})`.
           Then `t` is the number of ciphertext sub-blocks and `h` is the
           length of each binary string sub-block `c_i`.
        #. Let `(p, q, a, b)` be the private key whose corresponding
           public key is `n = pq`. Note that `\gcd(p, q) = ap + bq = 1`.
        #. Compute `d_1 = ((p + 1) / 4)^{t+1} \bmod{(p - 1)}`.
        #. Compute `d_2 = ((q + 1) / 4)^{t+1} \bmod{(q - 1)}`.
        #. Let `u = x_{t+1}^{d_1} \bmod p`.
        #. Let `v = x_{t+1}^{d_2} \bmod q`.
        #. Compute `x_0 = vap + ubq \bmod n`.
        #. For `i` from 1 to `t`, do:

           #. Compute `x_i = x_{t-1}^2 \bmod n`.
           #. Let `p_i` be the `h` least significant bits of `x_i`.
           #. Compute `m_i = p_i \oplus c_i`.

        #. The plaintext is `m = m_1 m_2 \cdots m_t`.

        EXAMPLES:

        The following decryption example is taken from Example 8.57, pages
        309--310 of [MenezesEtAl1996]_. Here we decrypt a binary string::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: bg = BlumGoldwasser()
            sage: p = 499; q = 547
            sage: C = ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680)
            sage: K = bg.private_key(p, q); K
            (499, 547, -57, 52)
            sage: P = bg.decrypt(C, K); P
            [[1, 0, 0, 1], [1, 1, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0], [1, 1, 0, 0]]

        Convert the plaintext sub-blocks into a binary string::

            sage: bin = BinaryStrings()
            sage: bin(flatten(P))
            10011100000100001100

        Decrypt a longer ciphertext and convert the resulting plaintext
        into an ASCII string::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: from sage.crypto.util import bin_to_ascii
            sage: bg = BlumGoldwasser()
            sage: p = 78307; q = 412487
            sage: K = bg.private_key(p, q)
            sage: C = ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \
            ...   [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \
            ...   [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \
            ...   [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \
            ...   [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \
            ...   [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \
            ...   [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \
            ...   [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \
            ...   [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \
            ...   [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \
            ...   [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \
            ...   [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \
            ...   [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279)
            sage: P = bg.decrypt(C, K)
            sage: bin_to_ascii(flatten(P))
            'Blum-Goldwasser encryption'

        TESTS:

        The private key `K = (p, q, a, b)` must be such that `p` and `q` are
        distinct Blum primes. Even if `p` and `q` pass this criterion, they
        must also satisfy the requirement `\gcd(p, q) = ap + bq = 1`. ::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: bg = BlumGoldwasser()
            sage: C = ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680)
            sage: K = (7, 7, 1, 2)
            sage: bg.decrypt(C, K)
            Traceback (most recent call last):
            ...
            ValueError: p and q must be distinct Blum primes.
            sage: K = (7, 23, 1, 2)
            sage: bg.decrypt(C, K)
            Traceback (most recent call last):
            ...
            ValueError: a and b must satisfy gcd(p, q) = ap + bq = 1.
            sage: K = (11, 29, 8, -3)
            sage: bg.decrypt(C, K)
            Traceback (most recent call last):
            ...
            ValueError: p and q must be distinct Blum primes.
        """
        # ciphertext
        c = C[0]
        xt1 = C[-1]
        # number of ciphertext sub-blocks
        t = len(c)
        # length of each ciphertext sub-block
        h = len(c[0])
        # private key
        p, q, a, b = K
        # public key
        n = p * q
        # sanity checks
        if p == q:
            raise ValueError("p and q must be distinct Blum primes.")
        if (a * p + b * q) != 1:
            raise ValueError("a and b must satisfy gcd(p, q) = ap + bq = 1.")
        if (not is_blum_prime(p)) or (not is_blum_prime(q)):
            raise ValueError("p and q must be distinct Blum primes.")
        # prepare to decrypt
        d1 = power_mod((p + 1) // 4, t + 1, p - 1)
        d2 = power_mod((q + 1) // 4, t + 1, q - 1)
        u = power_mod(xt1, d1, p)
        v = power_mod(xt1, d2, q)
        x0 = mod(v * a * p + u * b * q, n).lift()
        # perform the decryption
        M = []
        for i in xrange(t):
            x1 = power_mod(x0, 2, n)
            p = least_significant_bits(x1, h)
            M.append(list(map(xor, p, c[i])))
            x0 = x1
        return M
Example #28
0
def _partial_normal_form_of_block(G):
    r"""
    Return the partial normal form of the homogeneous block ``G``.

    For internal use in :meth:`_two_adic_normal_forms`.

    INPUT:

    - ``G`` -- a modular symmetric matrix over the `2`-adic integers

    OUTPUT:

    - ``D, B, w`` -- with ``B`` a transformation matrix such that
      ``B * G * B.T`` is in partial normal form
      and `w = 0, 1, 2` is the size of the part consisting of forms of type W

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _partial_normal_form_of_block
        sage: R = Zp(2,prec=4, type = 'fixed-mod',print_mode='terse', show_prec=False)
        sage: U = Matrix(R, 2, [0,1,1,0])
        sage: V = Matrix(R, 2, [2,1,1,2])
        sage: W1 = Matrix(R, 1, [1])
        sage: W3 = Matrix(R, 1, [3])
        sage: W5 = Matrix(R, 1, [5])
        sage: W7 = Matrix(R, 1, [7])
        sage: G = Matrix.block_diagonal([W1, U, V, W5, V, W3, V, W7])
        sage: B = _partial_normal_form_of_block(G)[1]
        sage: B * G * B.T
        [0 1 0 0 0 0 0 0 0 0 0 0]
        [1 0 0 0 0 0 0 0 0 0 0 0]
        [0 0 0 1 0 0 0 0 0 0 0 0]
        [0 0 1 0 0 0 0 0 0 0 0 0]
        [0 0 0 0 0 1 0 0 0 0 0 0]
        [0 0 0 0 1 0 0 0 0 0 0 0]
        [0 0 0 0 0 0 0 1 0 0 0 0]
        [0 0 0 0 0 0 1 0 0 0 0 0]
        [0 0 0 0 0 0 0 0 2 1 0 0]
        [0 0 0 0 0 0 0 0 1 2 0 0]
        [0 0 0 0 0 0 0 0 0 0 1 0]
        [0 0 0 0 0 0 0 0 0 0 0 7]
        sage: G = Matrix.block_diagonal([W1, U, V, W1, V, W1, V, W7])
        sage: B = _partial_normal_form_of_block(G)[1]
        sage: B * G * B.T
        [0 1 0 0 0 0 0 0 0 0 0 0]
        [1 0 0 0 0 0 0 0 0 0 0 0]
        [0 0 0 1 0 0 0 0 0 0 0 0]
        [0 0 1 0 0 0 0 0 0 0 0 0]
        [0 0 0 0 0 1 0 0 0 0 0 0]
        [0 0 0 0 1 0 0 0 0 0 0 0]
        [0 0 0 0 0 0 0 1 0 0 0 0]
        [0 0 0 0 0 0 1 0 0 0 0 0]
        [0 0 0 0 0 0 0 0 0 1 0 0]
        [0 0 0 0 0 0 0 0 1 0 0 0]
        [0 0 0 0 0 0 0 0 0 0 3 0]
        [0 0 0 0 0 0 0 0 0 0 0 7]
    """
    D = copy(G)
    n = D.ncols()
    B = copy(G.parent().identity_matrix())  # the transformation matrix
    blocks = _get_small_block_indices(D)
    # collect the indices of forms of types U, V and W
    U = []
    V = []
    W = []
    for i in blocks:
        if i + 1 in blocks or i == n - 1:
            W.append(i)
        else:
            if D[i, i] != 0:
                V += [i, i + 1]
            else:
                U += [i, i + 1]
        if len(W) == 3:
            # W W W transforms to W U or W V
            B[W, :] = _relations(D[W, W], 2) * B[W, :]
            D = B * G * B.T
            if mod(D[W[1:], W[1:]].det().unit_part(), 8) == 3:
                V += W[1:]
            else:
                U += W[1:]
            W = W[:1]
        if len(V) == 4:
            B[V, :] = _relations(D[V, V], 3) * B[V, :]
            U += V
            V = []
            D = B * G * B.T
    # put everything into the right order
    UVW = U + V + W
    B = B[UVW, :]
    D = B * G * B.T
    return D, B, len(W)
Example #29
0
    def encrypt(self, P, K, seed=None):
        r"""
        Apply the Blum-Goldwasser scheme to encrypt the plaintext ``P`` using
        the public key ``K``.

        INPUT:

        - ``P`` -- a non-empty string of plaintext. The string ``""`` is
          an empty string, whereas ``" "`` is a string consisting of one
          white space character. The plaintext can be a binary string or
          a string of ASCII characters. Where ``P`` is an ASCII string, then
          ``P`` is first encoded as a binary string prior to encryption.

        - ``K`` -- a public key, which is the product of two Blum primes.

        - ``seed`` -- (default: ``None``) if `p` and `q` are Blum primes and
          `n = pq` is a public key, then ``seed`` is a quadratic residue in
          the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. If ``seed=None``,
          then the function would generate its own random quadratic residue
          in `(\ZZ/n\ZZ)^{\ast}`. Where a value for ``seed`` is provided,
          it is your responsibility to ensure that the seed is a
          quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`.

        OUTPUT:

        - The ciphertext resulting from encrypting ``P`` using the public
          key ``K``. The ciphertext `C` is of the form
          `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Each `c_i` is a
          sub-block of binary string and `x_{t+1}` is the result of the
          `t+1`-th iteration of the Blum-Blum-Shub algorithm.

        ALGORITHM:

        The Blum-Goldwasser encryption algorithm is described in Algorithm
        8.56, page 309 of [MenezesEtAl1996]_. The algorithm works as follows:

        #. Let `n` be a public key, where `n = pq` is the product of two
           distinct Blum primes `p` and `q`.
        #. Let `k = \lfloor \log_2(n) \rfloor` and
           `h = \lfloor \log_2(k) \rfloor`.
        #. Let `m = m_1 m_2 \cdots m_t` be the message (plaintext) where
           each `m_i` is a binary string of length `h`.
        #. Choose a random seed `x_0`, which is a quadratic residue in
           the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. That is, choose
           a random `r \in (\ZZ/n\ZZ)^{\ast}` and compute
           `x_0 = r^2 \bmod n`.
        #. For `i` from 1 to `t`, do:

           #. Let `x_i = x_{i-1}^2 \bmod n`.
           #. Let `p_i` be the `h` least significant bits of `x_i`.
           #. Let `c_i = p_i \oplus m_i`.

        #. Compute `x_{t+1} = x_t^2 \bmod n`.
        #. The ciphertext is `c = (c_1, c_2, \dots, c_t, x_{t+1})`.

        The value `h` in the algorithm is the sub-block length. If the
        binary string representing the message cannot be divided into blocks
        of length `h` each, then other sub-block lengths would be used
        instead. The sub-block lengths to fall back on are in the
        following order: 16, 8, 4, 2, 1.

        EXAMPLES:

        The following encryption example is taken from Example 8.57,
        pages 309--310 of [MenezesEtAl1996]_. Here, we encrypt a binary
        string::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: bg = BlumGoldwasser()
            sage: p = 499; q = 547; n = p * q
            sage: P = "10011100000100001100"
            sage: C = bg.encrypt(P, n, seed=159201); C
            ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680)

        Convert the ciphertext sub-blocks into a binary string::

            sage: bin = BinaryStrings()
            sage: bin(flatten(C[0]))
            00100000110011100100

        Now encrypt an ASCII string. The result is random; no seed is
        provided to the encryption function so the function generates its
        own random seed::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: bg = BlumGoldwasser()
            sage: K = 32300619509
            sage: P = "Blum-Goldwasser encryption"
            sage: bg.encrypt(P, K)  # random
            ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \
            [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \
            [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \
            [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \
            [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \
            [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \
            [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \
            [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \
            [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \
            [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \
            [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \
            [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279)

        TESTS:

        The plaintext cannot be an empty string. ::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: bg = BlumGoldwasser()
            sage: bg.encrypt("", 3)
            Traceback (most recent call last):
            ...
            ValueError: The plaintext cannot be an empty string.
        """
        # sanity check
        if P == "":
            raise ValueError("The plaintext cannot be an empty string.")
        n = K
        k = floor(log(n, base=2))
        h = floor(log(k, base=2))
        bin = BinaryStrings()
        M = ""
        try:
            # the plaintext is a binary string
            M = bin(P)
        except TypeError:
            # encode the plaintext as a binary string
            # An exception might be raised here if P cannot be encoded as a
            # binary string.
            M = bin.encoding(P)
        # the number of plaintext sub-blocks; each sub-block has length h
        t = 0
        try:
            # Attempt to use t and h values from the algorithm described
            # in [MenezesEtAl1996].
            t = len(M) / h
            # If the following raises an exception, then we can't use
            # the t and h values specified by [MenezesEtAl1996].
            mod(len(M), t)
        # fall back to using other sub-block lengths
        except TypeError:
            # sub-blocks of length h = 16
            if mod(len(M), 16) == 0:
                h = 16
                t = len(M) // h
            # sub-blocks of length h = 8
            elif mod(len(M), 8) == 0:
                h = 8
                t = len(M) // h
            # sub-blocks of length h = 4
            elif mod(len(M), 4) == 0:
                h = 4
                t = len(M) // h
            # sub-blocks of length h = 2
            elif mod(len(M), 2) == 0:
                h = 2
                t = len(M) // h
            # sub-blocks of length h = 1
            else:
                h = 1
                t = len(M) // h
        # If no seed is provided, select a random seed.
        x0 = seed
        if seed is None:
            zmod = IntegerModRing(n)  # K = n = pq
            r = zmod.random_element().lift()
            while gcd(r, n) != 1:
                r = zmod.random_element().lift()
            x0 = power_mod(r, 2, n)
        # perform the encryption
        to_int = lambda x: int(str(x))
        C = []
        for i in xrange(t):
            x1 = power_mod(x0, 2, n)
            p = least_significant_bits(x1, h)
            # xor p with a sub-block of length h. There are t sub-blocks of
            # length h each.
            C.append(
                list(map(xor, p, [to_int(_) for _ in M[i * h:(i + 1) * h]])))
            x0 = x1
        x1 = power_mod(x0, 2, n)
        return (C, x1)
Example #30
0
def _two_adic_normal_forms(G, partial=False):
    r"""
    Return the 2-adic normal form of a symmetric matrix.

    INPUT:

    - ``G`` -- block diagonal matrix with blocks of type `U`, `V`, `W`
    - ``partial`` -- bool (defaul: ``False``)

    OUTPUT:

    - ``D``, ``B`` -- such that ``D = B * G * B.T``

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _two_adic_normal_forms
        sage: R = Zp(2, type = 'fixed-mod', print_mode='terse', show_prec=False)
        sage: U = Matrix(R,2,[0,1,1,0])
        sage: V = Matrix(R,2,[2,1,1,2])
        sage: W1 = Matrix(R,1,[1])
        sage: W3 = Matrix(R,1,[3])
        sage: W5 = Matrix(R,1,[5])
        sage: W7 = Matrix(R,1,[7])
        sage: G = Matrix.block_diagonal([2*W1,2*W1,4*V])
        sage: B = _two_adic_normal_forms(G)[1]
        sage: B * G * B.T
        [ 2  0  0  0]
        [ 0 10  0  0]
        [ 0  0  0  4]
        [ 0  0  4  0]
        sage: G = Matrix.block_diagonal([W1,2*V,2*W3,2*W5])
        sage: B = _two_adic_normal_forms(G)[1]
        sage: B * G * B.T
        [3 0 0 0 0]
        [0 0 2 0 0]
        [0 2 0 0 0]
        [0 0 0 2 0]
        [0 0 0 0 2]
        sage: G = Matrix.block_diagonal([U,2*V,2*W3,2*W5])
        sage: B = _two_adic_normal_forms(G)[1]
        sage: B * G * B.T
        [2 1 0 0 0 0]
        [1 2 0 0 0 0]
        [0 0 4 2 0 0]
        [0 0 2 4 0 0]
        [0 0 0 0 2 0]
        [0 0 0 0 0 6]
    """
    B = copy(G.parent().identity_matrix())
    h, scales = _get_homogeneous_block_indices(G)
    h.append(B.ncols())
    # UVlist[k] is a list of indices of the block of scale p^k.
    # It contains the indices of the part of types U or V.
    # So it may be empty.
    UVlist = [[], []]  # empty lists are appended to avoid special cases.
    # same as UVlist but contains the indices of the part of type W
    Wlist = [[], []]
    # homogeneous normal form for each part
    for k in range(scales[-1] - scales[0] + 1):
        if k + scales[0] in scales:
            i = scales.index(k + scales[0])
            Gk = G[h[i]:h[i + 1], h[i]:h[i + 1]]
            Dk, Bk, wk = _partial_normal_form_of_block(Gk)
            B[h[i]:h[i + 1], :] = Bk * B[h[i]:h[i + 1], :]
            if not partial:
                Dk, B1k = _homogeneous_normal_form(Dk, wk)
                B[h[i]:h[i + 1], :] = B1k * B[h[i]:h[i + 1], :]
            UVlist.append(list(range(h[i], h[i + 1] - wk)))
            Wlist.append(list(range(h[i + 1] - wk, h[i + 1])))
        else:
            UVlist.append([])
            Wlist.append([])
    D = B * G * B.T
    if partial:
        return D, B
    # use relations descending in k
    # we never leave partial normal form
    # but the homogeneous normal form may be destroyed
    # it is restored at the end.
    for k in range(len(UVlist) - 1, 2, -1):
        # setup notation
        W = Wlist[k]
        Wm = Wlist[k - 1]
        Wmm = Wlist[k - 2]
        UV = UVlist[k]
        UVm = UVlist[k - 1]
        V = UVlist[k][-2:]
        if len(V) != 0 and D[V[0], V[0]] == 0:
            V = []  # it is U not V
        # condition b)
        if len(Wm) != 0:
            if len(V) == 2:
                R = Wm[:1] + V
                B[R, :] = _relations(D[R, R], 7) * B[R, :]
                V = []
                D = B * G * B.T
            E = {3, 7}
            for w in W:
                if D[w, w].unit_part() in E:
                    R = Wm[:1] + [w]
                    B[R, :] = _relations(D[R, R], 6) * B[R, :]
                    D = B * G * B.T
        # condition c)
        # We want type a or W = []
        # modify D[w,w] to go from type b to type a
        x = [len(V)] + [ZZ(mod(w.unit_part(), 8)) for w in D[W, W].diagonal()]
        x.sort()
        # a = [[0,1], [2,3], [2,5], [0,7], [0,1,1], [1,2,3], [0,7,7], [0,1,7]]
        b = [[0, 5], [2, 7], [1, 2], [0, 3], [0, 1, 5], [1, 2, 7], [0, 3, 7],
             [0, 1, 3]]
        if x in b:
            w = W[-1]
            if x == [3, 7]:
                w = W[0]
            if len(UVm) > 0:
                R = UVm[-2:] + [w]
                B[R, :] = _relations(D[R, R], 8) * B[R, :]
            elif len(Wmm) > 0:
                R = Wmm[:1] + [w]
                B[R, :] = _relations(D[R, R], 10) * B[R, :]
            elif len(Wm) == 2:
                e0 = D[Wm, Wm][0, 0].unit_part()
                e1 = D[Wm, Wm][1, 1].unit_part()
                if mod(e1 - e0, 4) == 0:
                    R = Wm + [w]
                    B[R, :] = _relations(D[R, R], 9) * B[R, :]
        D = B * G * B.T
        # condition a) - stay in homogeneous normal form
        R = UV + W
        Dk = D[R, R]
        Bk = _homogeneous_normal_form(Dk, len(W))[1]
        B[R, :] = Bk * B[R, :]
        D = B * G * B.T
        # we need to restore the homogeneous normal form of  k-1
        if len(Wm) > 0:
            R = UVm + Wm
            Dkm = D[R, R]
            Bkm = _homogeneous_normal_form(Dkm, len(Wm))[1]
            B[R, :] = Bkm * B[R, :]
            D = B * G * B.T
    return D, B
Example #31
0
    def decrypt(self, C, K):
        r"""
        Apply the Blum-Goldwasser scheme to decrypt the ciphertext ``C``
        using the private key ``K``.

        INPUT:

        - ``C`` -- a ciphertext resulting from encrypting a plaintext using
          the Blum-Goldwasser encryption algorithm. The ciphertext `C` must
          be of the form `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Each `c_i`
          is a sub-block of binary string and `x_{t+1}` is the result of the
          `t+1`-th iteration of the Blum-Blum-Shub algorithm.

        - ``K`` -- a private key `(p, q, a, b)` where `p` and `q` are
          distinct Blum primes and `\gcd(p, q) = ap + bq = 1`.

        OUTPUT:

        - The plaintext resulting from decrypting the ciphertext ``C`` using
          the Blum-Goldwasser decryption algorithm.

        ALGORITHM:

        The Blum-Goldwasser decryption algorithm is described in Algorithm
        8.56, page 309 of [MvOV1996]_. The algorithm works as follows:

        #. Let `C` be the ciphertext `C = (c_1, c_2, \dots, c_t, x_{t+1})`.
           Then `t` is the number of ciphertext sub-blocks and `h` is the
           length of each binary string sub-block `c_i`.
        #. Let `(p, q, a, b)` be the private key whose corresponding
           public key is `n = pq`. Note that `\gcd(p, q) = ap + bq = 1`.
        #. Compute `d_1 = ((p + 1) / 4)^{t+1} \bmod{(p - 1)}`.
        #. Compute `d_2 = ((q + 1) / 4)^{t+1} \bmod{(q - 1)}`.
        #. Let `u = x_{t+1}^{d_1} \bmod p`.
        #. Let `v = x_{t+1}^{d_2} \bmod q`.
        #. Compute `x_0 = vap + ubq \bmod n`.
        #. For `i` from 1 to `t`, do:

           #. Compute `x_i = x_{t-1}^2 \bmod n`.
           #. Let `p_i` be the `h` least significant bits of `x_i`.
           #. Compute `m_i = p_i \oplus c_i`.

        #. The plaintext is `m = m_1 m_2 \cdots m_t`.

        EXAMPLES:

        The following decryption example is taken from Example 8.57, pages
        309--310 of [MvOV1996]_. Here we decrypt a binary string::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: bg = BlumGoldwasser()
            sage: p = 499; q = 547
            sage: C = ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680)
            sage: K = bg.private_key(p, q); K
            (499, 547, -57, 52)
            sage: P = bg.decrypt(C, K); P
            [[1, 0, 0, 1], [1, 1, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0], [1, 1, 0, 0]]

        Convert the plaintext sub-blocks into a binary string::

            sage: bin = BinaryStrings()
            sage: bin(flatten(P))
            10011100000100001100

        Decrypt a longer ciphertext and convert the resulting plaintext
        into an ASCII string::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: from sage.crypto.util import bin_to_ascii
            sage: bg = BlumGoldwasser()
            sage: p = 78307; q = 412487
            sage: K = bg.private_key(p, q)
            sage: C = ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \
            ....: [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \
            ....: [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \
            ....: [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \
            ....: [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \
            ....: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \
            ....: [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \
            ....: [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \
            ....: [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \
            ....: [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \
            ....: [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \
            ....: [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \
            ....: [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279)
            sage: P = bg.decrypt(C, K)
            sage: bin_to_ascii(flatten(P))
            'Blum-Goldwasser encryption'

        TESTS:

        The private key `K = (p, q, a, b)` must be such that `p` and `q` are
        distinct Blum primes. Even if `p` and `q` pass this criterion, they
        must also satisfy the requirement `\gcd(p, q) = ap + bq = 1`. ::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: bg = BlumGoldwasser()
            sage: C = ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680)
            sage: K = (7, 7, 1, 2)
            sage: bg.decrypt(C, K)
            Traceback (most recent call last):
            ...
            ValueError: p and q must be distinct Blum primes.
            sage: K = (7, 23, 1, 2)
            sage: bg.decrypt(C, K)
            Traceback (most recent call last):
            ...
            ValueError: a and b must satisfy gcd(p, q) = ap + bq = 1.
            sage: K = (11, 29, 8, -3)
            sage: bg.decrypt(C, K)
            Traceback (most recent call last):
            ...
            ValueError: p and q must be distinct Blum primes.
        """
        # ciphertext
        c = C[0]
        xt1 = C[-1]
        # number of ciphertext sub-blocks
        t = len(c)
        # length of each ciphertext sub-block
        h = len(c[0])
        # private key
        p, q, a, b = K
        # public key
        n = p * q
        # sanity checks
        if p == q:
            raise ValueError("p and q must be distinct Blum primes.")
        if (a*p + b*q) != 1:
            raise ValueError("a and b must satisfy gcd(p, q) = ap + bq = 1.")
        if (not is_blum_prime(p)) or (not is_blum_prime(q)):
            raise ValueError("p and q must be distinct Blum primes.")
        # prepare to decrypt
        d1 = power_mod((p + 1) // 4, t + 1, p - 1)
        d2 = power_mod((q + 1) // 4, t + 1, q - 1)
        u = power_mod(xt1, d1, p)
        v = power_mod(xt1, d2, q)
        x0 = mod(v*a*p + u*b*q, n).lift()
        # perform the decryption
        M = []
        for i in range(t):
            x1 = power_mod(x0, 2, n)
            p = least_significant_bits(x1, h)
            M.append(list(map(xor, p, c[i])))
            x0 = x1
        return M
Example #32
0
def _normalize_2x2(G):
    r"""
    Normalize this indecomposable `2` by `2` block.

    INPUT:

    ``G`` - a `2` by `2` matrix over `\ZZ_p`
    with ``type = 'fixed-mod'`` of the form::

        [2a  b]
        [ b 2c] * 2^n

    with `b` of valuation 1.

    OUTPUT:

    A unimodular `2` by `2` matrix ``B`` over `\ZZ_p` with
    ``B * G * B.transpose()``
    either::

        [0 1]              [2 1]
        [1 0] * 2^n  or    [1 2] * 2^n

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _normalize_2x2
        sage: R = Zp(2, prec = 15, type = 'fixed-mod', print_mode='series', show_prec=False)
        sage: G = Matrix(R, 2, [-17*2,3,3,23*2])
        sage: B =_normalize_2x2(G)
        sage: B * G * B.T
        [2 1]
        [1 2]

        sage: G = Matrix(R,2,[-17*4,3,3,23*2])
        sage: B = _normalize_2x2(G)
        sage: B*G*B.T
        [0 1]
        [1 0]

        sage: G = 2^3 * Matrix(R, 2, [-17*2,3,3,23*2])
        sage: B = _normalize_2x2(G)
        sage: B * G * B.T
        [2^4 2^3]
        [2^3 2^4]
    """
    from sage.rings.all import PolynomialRing
    from sage.modules.free_module_element import vector
    B = copy(G.parent().identity_matrix())
    R = G.base_ring()
    P = PolynomialRing(R, 'x')
    x = P.gen()

    # The input must be an even block
    odd1 = (G[0, 0].valuation() < G[1, 0].valuation())
    odd2 = (G[1, 1].valuation() < G[1, 0].valuation())
    if odd1 or odd2:
        raise ValueError("Not a valid 2 x 2 block.")
    scale = 2**G[0, 1].valuation()
    D = Matrix(R, 2, 2, [d // scale for d in G.list()])
    # now D is of the form
    # [2a b ]
    # [b  2c]
    # where b has valuation 1.
    G = copy(D)

    # Make sure G[1, 1] has valuation 1.
    if D[1, 1].valuation() > D[0, 0].valuation():
        B.swap_columns(0, 1)
        D.swap_columns(0, 1)
        D.swap_rows(0, 1)
    if D[1, 1].valuation() != 1:
        # this works because
        # D[0, 0] has valuation at least 2
        B[1, :] += B[0, :]
        D = B * G * B.transpose()
    assert D[1, 1].valuation() == 1

    if mod(D.det(), 8) == 3:
        #  in this case we can transform D to
        #  2 1
        #  1 2
        # Find a point of norm 2
        # solve: 2 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0]
        pol = (D[1, 1] * x**2 + 2 * D[1, 0] * x + D[0, 0] - 2) // 2
        # somehow else pari can get a hickup see trac #24065
        pol = pol // pol.leading_coefficient()
        sol = pol.roots()[0][0]
        B[0, 1] = sol
        D = B * G * B.transpose()
        # make D[0, 1] = 1
        B[1, :] *= D[1, 0].inverse_of_unit()
        D = B * G * B.transpose()

        # solve: v*D*v == 2 with v = (x, -2*x+1)
        if D[1, 1] != 2:
            v = vector([x, -2 * x + 1])
            pol = (v * D * v - 2) // 2
            # somehow else pari can get a hickup see trac #24065
            pol = pol // pol.leading_coefficient()
            sol = pol.roots()[0][0]
            B[1, :] = sol * B[0, :] + (-2 * sol + 1) * B[1, :]
            D = B * G * B.transpose()
        # check the result
        assert D == Matrix(G.parent(), 2, 2, [2, 1, 1, 2]), "D1 \n %r" % D
    elif mod(D.det(), 8) == 7:
        # in this case we can transform D to
        #  0 1
        #  1 0
        # Find a point representing 0
        # solve: 0 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0]
        pol = (D[1, 1] * x**2 + 2 * D[1, 0] * x + D[0, 0]) // 2
        # somehow else pari can get a hickup, see trac #24065
        pol = pol // pol.leading_coefficient()
        sol = pol.roots()[0][0]
        B[0, :] += sol * B[1, :]
        D = B * G * B.transpose()
        # make the second basis vector have 0 square as well.
        B[1, :] = B[1, :] - D[1, 1] // (2 * D[0, 1]) * B[0, :]
        D = B * G * B.transpose()
        # rescale to get D[0,1] = 1
        B[0, :] *= D[1, 0].inverse_of_unit()
        D = B * G * B.transpose()
        # check the result
        assert D == Matrix(G.parent(), 2, 2, [0, 1, 1, 0]), "D2 \n %r" % D
    return B
Example #33
0
    def encrypt(self, P, K, seed=None):
        r"""
        Apply the Blum-Goldwasser scheme to encrypt the plaintext ``P`` using
        the public key ``K``.

        INPUT:

        - ``P`` -- a non-empty string of plaintext. The string ``""`` is
          an empty string, whereas ``" "`` is a string consisting of one
          white space character. The plaintext can be a binary string or
          a string of ASCII characters. Where ``P`` is an ASCII string, then
          ``P`` is first encoded as a binary string prior to encryption.

        - ``K`` -- a public key, which is the product of two Blum primes.

        - ``seed`` -- (default: ``None``) if `p` and `q` are Blum primes and
          `n = pq` is a public key, then ``seed`` is a quadratic residue in
          the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. If ``seed=None``,
          then the function would generate its own random quadratic residue
          in `(\ZZ/n\ZZ)^{\ast}`. Where a value for ``seed`` is provided,
          it is your responsibility to ensure that the seed is a
          quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`.

        OUTPUT:

        - The ciphertext resulting from encrypting ``P`` using the public
          key ``K``. The ciphertext `C` is of the form
          `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Each `c_i` is a
          sub-block of binary string and `x_{t+1}` is the result of the
          `t+1`-th iteration of the Blum-Blum-Shub algorithm.

        ALGORITHM:

        The Blum-Goldwasser encryption algorithm is described in Algorithm
        8.56, page 309 of [MvOV1996]_. The algorithm works as follows:

        #. Let `n` be a public key, where `n = pq` is the product of two
           distinct Blum primes `p` and `q`.
        #. Let `k = \lfloor \log_2(n) \rfloor` and
           `h = \lfloor \log_2(k) \rfloor`.
        #. Let `m = m_1 m_2 \cdots m_t` be the message (plaintext) where
           each `m_i` is a binary string of length `h`.
        #. Choose a random seed `x_0`, which is a quadratic residue in
           the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. That is, choose
           a random `r \in (\ZZ/n\ZZ)^{\ast}` and compute
           `x_0 = r^2 \bmod n`.
        #. For `i` from 1 to `t`, do:

           #. Let `x_i = x_{i-1}^2 \bmod n`.
           #. Let `p_i` be the `h` least significant bits of `x_i`.
           #. Let `c_i = p_i \oplus m_i`.

        #. Compute `x_{t+1} = x_t^2 \bmod n`.
        #. The ciphertext is `c = (c_1, c_2, \dots, c_t, x_{t+1})`.

        The value `h` in the algorithm is the sub-block length. If the
        binary string representing the message cannot be divided into blocks
        of length `h` each, then other sub-block lengths would be used
        instead. The sub-block lengths to fall back on are in the
        following order: 16, 8, 4, 2, 1.

        EXAMPLES:

        The following encryption example is taken from Example 8.57,
        pages 309--310 of [MvOV1996]_. Here, we encrypt a binary
        string::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: bg = BlumGoldwasser()
            sage: p = 499; q = 547; n = p * q
            sage: P = "10011100000100001100"
            sage: C = bg.encrypt(P, n, seed=159201); C
            ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680)

        Convert the ciphertext sub-blocks into a binary string::

            sage: bin = BinaryStrings()
            sage: bin(flatten(C[0]))
            00100000110011100100

        Now encrypt an ASCII string. The result is random; no seed is
        provided to the encryption function so the function generates its
        own random seed::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: bg = BlumGoldwasser()
            sage: K = 32300619509
            sage: P = "Blum-Goldwasser encryption"
            sage: bg.encrypt(P, K)  # random
            ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \
            [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \
            [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \
            [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \
            [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \
            [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \
            [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \
            [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \
            [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \
            [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \
            [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \
            [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279)

        TESTS:

        The plaintext cannot be an empty string. ::

            sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
            sage: bg = BlumGoldwasser()
            sage: bg.encrypt("", 3)
            Traceback (most recent call last):
            ...
            ValueError: The plaintext cannot be an empty string.
        """
        # sanity check
        if P == "":
            raise ValueError("The plaintext cannot be an empty string.")
        n = K
        k = floor(log(n, base=2))
        h = floor(log(k, base=2))
        bin = BinaryStrings()
        M = ""
        try:
            # the plaintext is a binary string
            M = bin(P)
        except TypeError:
            # encode the plaintext as a binary string
            # An exception might be raised here if P cannot be encoded as a
            # binary string.
            M = bin.encoding(P)
        # the number of plaintext sub-blocks; each sub-block has length h
        t = 0
        try:
            # Attempt to use t and h values from the algorithm described
            # in [MvOV1996].
            t = len(M) / h
            # If the following raises an exception, then we can't use
            # the t and h values specified by [MvOV1996].
            mod(len(M), t)
        # fall back to using other sub-block lengths
        except TypeError:
            # sub-blocks of length h = 16
            if mod(len(M), 16) == 0:
                h = 16
                t = len(M) // h
            # sub-blocks of length h = 8
            elif mod(len(M), 8) == 0:
                h = 8
                t = len(M) // h
            # sub-blocks of length h = 4
            elif mod(len(M), 4) == 0:
                h = 4
                t = len(M) // h
            # sub-blocks of length h = 2
            elif mod(len(M), 2) == 0:
                h = 2
                t = len(M) // h
            # sub-blocks of length h = 1
            else:
                h = 1
                t = len(M) // h
        # If no seed is provided, select a random seed.
        x0 = seed
        if seed is None:
            zmod = IntegerModRing(n)  # K = n = pq
            r = zmod.random_element().lift()
            while gcd(r, n) != 1:
                r = zmod.random_element().lift()
            x0 = power_mod(r, 2, n)
        # perform the encryption
        to_int = lambda x: int(str(x))
        C = []
        for i in range(t):
            x1 = power_mod(x0, 2, n)
            p = least_significant_bits(x1, h)
            # xor p with a sub-block of length h. There are t sub-blocks of
            # length h each.
            C.append(list(map(xor, p, [to_int(_) for _ in M[i*h : (i+1)*h]])))
            x0 = x1
        x1 = power_mod(x0, 2, n)
        return (C, x1)