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(map(xor, p, map(to_int, M[i*h : (i+1)*h])))
            x0 = x1
        x1 = power_mod(x0, 2, n)
        return (C, x1)
Exemple #2
0
def blum_blum_shub(length, seed=None, p=None, q=None,
                   lbound=None, ubound=None, ntries=100):
    r"""
    The Blum-Blum-Shub (BBS) pseudorandom bit generator.

    See the original paper by Blum, Blum and Shub [BlumBlumShub1986]_. The
    BBS algorithm is also discussed in section 5.5.2 of [MenezesEtAl1996]_.

    INPUT:

    - ``length`` -- positive integer; the number of bits in the output
      pseudorandom bit sequence.

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

    - ``p`` -- (default: ``None``) a large positive prime congruent to 3
      modulo 4. Both ``p`` and ``q`` must be distinct. If ``p=None``, then
      a value for ``p`` will be generated, where
      ``0 < lower_bound <= p <= upper_bound``.

    - ``q`` -- (default: ``None``) a large positive prime congruence to 3
      modulo 4. Both ``p`` and ``q`` must be distinct. If ``q=None``, then
      a value for ``q`` will be generated, where
      ``0 < lower_bound <= q <= upper_bound``.

    - ``lbound`` -- (positive integer, default: ``None``) the lower
      bound on how small each random primes `p` and `q` can be. So we
      have ``0 < lbound <= p, q <= ubound``. The lower bound must be
      distinct from the upper bound.

    - ``ubound`` -- (positive integer, default: ``None``) the upper
      bound on how large each random primes `p` and `q` can be. So we have
      ``0 < lbound <= p, q <= 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 pseudorandom bit sequence whose length is specified by ``length``.

    Here is a common use case for this function. If you want this
    function to use pre-computed values for `p` and `q`, you should pass
    those pre-computed values to this function. In that case, you only need
    to specify values for ``length``, ``p`` and ``q``, and you do not need
    to worry about doing anything with the parameters ``lbound`` and
    ``ubound``. The pre-computed values `p` and `q` must be Blum primes.
    It is your responsibility to check that both `p` and `q` are Blum primes.

    Here is another common use case. If you want the function to generate
    its own values for `p` and `q`, you must specify the lower and upper
    bounds within which these two primes must lie. In that case, you must
    specify values for ``length``, ``lbound`` and ``ubound``, and you do
    not need to worry about values for the parameters ``p`` and ``q``. The
    parameter ``ntries`` is only relevant when you want this function to
    generate ``p`` and ``q``.

    .. NOTE::

        Beware that there might not be any primes between the lower and
        upper bounds. So make sure that these two bounds are
        "sufficiently" far apart from each other for there to be primes
        congruent to 3 modulo 4. In particular, there should be at least
        two distinct primes within these bounds, each prime being congruent
        to 3 modulo 4. This function uses the function
        :func:`random_blum_prime() <sage.crypto.util.random_blum_prime>` to
        generate random primes that are congruent to 3 modulo 4.

    ALGORITHM:

    The BBS algorithm as described below is adapted from the presentation
    in Algorithm 5.40, page 186 of [MenezesEtAl1996]_.

    #. Let `L` be the desired number of bits in the output bit sequence.
       That is, `L` is the desired length of the bit string.
    #. Let `p` and `q` be two large distinct primes, each congruent to 3
       modulo 4.
    #. Let `n = pq` be the product of `p` and `q`.
    #. Select a random seed value `s \in (\ZZ/n\ZZ)^{\ast}`, where
       `(\ZZ/n\ZZ)^{\ast}` is the multiplicative group of `\ZZ/n\ZZ`.
    #. Let `x_0 = s^2 \bmod n`.
    #. For `i` from 1 to `L`, do

       #. Let `x_i = x_{i-1}^2 \bmod n`.
       #. Let `z_i` be the least significant bit of `x_i`.

    #. The output pseudorandom bit sequence is `z_1, z_2, \dots, z_L`.

    EXAMPLES:

    A BBS pseudorandom bit sequence with a specified seed::

        sage: from sage.crypto.stream import blum_blum_shub
        sage: blum_blum_shub(length=6, seed=3, p=11, q=19)
        110000

    You could specify the length of the bit string, with given values for
    ``p`` and ``q``::

        sage: blum_blum_shub(length=6, p=11, q=19)  # random
        001011

    Or you could specify the length of the bit string, with given values for
    the lower and upper bounds::

        sage: blum_blum_shub(length=6, lbound=10**4, ubound=10**5)  # random
        110111

    Under some reasonable hypotheses, Blum-Blum-Shub [BlumBlumShub1982]_
    sketch a proof that the period of the BBS stream cipher is equal to
    `\lambda(\lambda(n))`, where `\lambda(n)` is the Carmichael function of
    `n`. This is verified below in a few examples by using the function
    :func:`lfsr_connection_polynomial() <sage.crypto.lfsr.lfsr_connection_polynomial>`
    (written by Tim Brock) which computes the connection polynomial of a
    linear feedback shift register sequence. The degree of that polynomial
    is the period. ::

        sage: from sage.crypto.stream import blum_blum_shub
        sage: from sage.crypto.util import carmichael_lambda
        sage: carmichael_lambda(carmichael_lambda(7*11))
        4
        sage: s = [GF(2)(int(str(x))) for x in blum_blum_shub(60, p=7, q=11, seed=13)]
        sage: lfsr_connection_polynomial(s)
        x^3 + x^2 + x + 1
        sage: carmichael_lambda(carmichael_lambda(11*23))
        20
        sage: s = [GF(2)(int(str(x))) for x in blum_blum_shub(60, p=11, q=23, seed=13)]
        sage: lfsr_connection_polynomial(s)
        x^19 + x^18 + x^17 + x^16 + x^15 + x^14 + x^13 + x^12 + x^11 + x^10 + x^9 + x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1

    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.stream import blum_blum_shub
        sage: blum_blum_shub(6, lbound=24, ubound=30, ntries=10)
        Traceback (most recent call last):
        ...
        ValueError: No Blum primes within the specified closed interval.

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

        sage: blum_blum_shub(6, lbound=2, ubound=3)
        Traceback (most recent call last):
        ...
        ValueError: Both the lower and upper bounds must be > 2.
        sage: blum_blum_shub(6, lbound=3, ubound=2)
        Traceback (most recent call last):
        ...
        ValueError: Both the lower and upper bounds must be > 2.
        sage: blum_blum_shub(6, lbound=2, ubound=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: blum_blum_shub(6, lbound=3, ubound=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: blum_blum_shub(6, lbound=4, ubound=3)
        Traceback (most recent call last):
        ...
        ValueError: The lower bound must be less than the upper bound.

    REFERENCES:

    .. [BlumBlumShub1982] L. Blum, M. Blum, and M. Shub.
      Comparison of Two Pseudo-Random Number Generators.
      *Advances in Cryptology: Proceedings of Crypto '82*,
      pp.61--78, 1982.

    .. [BlumBlumShub1986] L. Blum, M. Blum, and M. Shub.
      A Simple Unpredictable Pseudo-Random Number Generator.
      *SIAM Journal on Computing*, 15(2):364--383, 1986.
    """
    # sanity checks
    if length < 0:
        raise ValueError("The length of the bit string must be positive.")
    if (p is None) and (p == q == lbound == ubound):
        raise ValueError("Either specify values for p and q, or specify values for the lower and upper bounds.")
    # Use pre-computed Blum primes. Both the parameters p and q are
    # assumed to be Blum primes. No attempts are made to ensure that they
    # are indeed Blum primes.
    randp = 0
    randq = 0
    if (p is not None) and (q is not None):
        randp = p
        randq = q
    # generate random Blum primes within specified bounds
    elif (lbound is not None) and (ubound is not None):
        randp = random_blum_prime(lbound, ubound, ntries=ntries)
        randq = random_blum_prime(lbound, ubound, ntries=ntries)
        while randp == randq:
            randq = random_blum_prime(lbound, ubound, ntries=ntries)
    # no pre-computed primes given, and no appropriate bounds given
    else:
        raise ValueError("Either specify values for p and q, or specify values for the lower and upper bounds.")
    # By now, we should have two distinct Blum primes.
    n = randp * randq
    # If no seed is provided, select a random seed.
    x0 = seed
    if seed is None:
        zmod = IntegerModRing(n)
        s = zmod.random_element().lift()
        while gcd(s, n) != 1:
            s = zmod.random_element().lift()
        x0 = power_mod(s, 2, n)
    # start generating pseudorandom bits
    z = []
    for i in xrange(length):
        x1 = power_mod(x0, 2, n)
        z.append(x1 % 2)
        x0 = x1
    bin = BinaryStrings()
    return bin(z)
    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(map(xor, p, c[i]))
            x0 = x1
        return M
Exemple #4
0
def blum_blum_shub(length,
                   seed=None,
                   p=None,
                   q=None,
                   lbound=None,
                   ubound=None,
                   ntries=100):
    r"""
    The Blum-Blum-Shub (BBS) pseudorandom bit generator.

    See the original paper by Blum, Blum and Shub [BlumBlumShub1986]_. The
    BBS algorithm is also discussed in section 5.5.2 of [MenezesEtAl1996]_.

    INPUT:

    - ``length`` -- positive integer; the number of bits in the output
      pseudorandom bit sequence.

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

    - ``p`` -- (default: ``None``) a large positive prime congruent to 3
      modulo 4. Both ``p`` and ``q`` must be distinct. If ``p=None``, then
      a value for ``p`` will be generated, where
      ``0 < lower_bound <= p <= upper_bound``.

    - ``q`` -- (default: ``None``) a large positive prime congruence to 3
      modulo 4. Both ``p`` and ``q`` must be distinct. If ``q=None``, then
      a value for ``q`` will be generated, where
      ``0 < lower_bound <= q <= upper_bound``.

    - ``lbound`` -- (positive integer, default: ``None``) the lower
      bound on how small each random primes `p` and `q` can be. So we
      have ``0 < lbound <= p, q <= ubound``. The lower bound must be
      distinct from the upper bound.

    - ``ubound`` -- (positive integer, default: ``None``) the upper
      bound on how large each random primes `p` and `q` can be. So we have
      ``0 < lbound <= p, q <= 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 pseudorandom bit sequence whose length is specified by ``length``.

    Here is a common use case for this function. If you want this
    function to use pre-computed values for `p` and `q`, you should pass
    those pre-computed values to this function. In that case, you only need
    to specify values for ``length``, ``p`` and ``q``, and you do not need
    to worry about doing anything with the parameters ``lbound`` and
    ``ubound``. The pre-computed values `p` and `q` must be Blum primes.
    It is your responsibility to check that both `p` and `q` are Blum primes.

    Here is another common use case. If you want the function to generate
    its own values for `p` and `q`, you must specify the lower and upper
    bounds within which these two primes must lie. In that case, you must
    specify values for ``length``, ``lbound`` and ``ubound``, and you do
    not need to worry about values for the parameters ``p`` and ``q``. The
    parameter ``ntries`` is only relevant when you want this function to
    generate ``p`` and ``q``.

    .. NOTE::

        Beware that there might not be any primes between the lower and
        upper bounds. So make sure that these two bounds are
        "sufficiently" far apart from each other for there to be primes
        congruent to 3 modulo 4. In particular, there should be at least
        two distinct primes within these bounds, each prime being congruent
        to 3 modulo 4. This function uses the function
        :func:`random_blum_prime() <sage.crypto.util.random_blum_prime>` to
        generate random primes that are congruent to 3 modulo 4.

    ALGORITHM:

    The BBS algorithm as described below is adapted from the presentation
    in Algorithm 5.40, page 186 of [MenezesEtAl1996]_.

    #. Let `L` be the desired number of bits in the output bit sequence.
       That is, `L` is the desired length of the bit string.
    #. Let `p` and `q` be two large distinct primes, each congruent to 3
       modulo 4.
    #. Let `n = pq` be the product of `p` and `q`.
    #. Select a random seed value `s \in (\ZZ/n\ZZ)^{\ast}`, where
       `(\ZZ/n\ZZ)^{\ast}` is the multiplicative group of `\ZZ/n\ZZ`.
    #. Let `x_0 = s^2 \bmod n`.
    #. For `i` from 1 to `L`, do

       #. Let `x_i = x_{i-1}^2 \bmod n`.
       #. Let `z_i` be the least significant bit of `x_i`.

    #. The output pseudorandom bit sequence is `z_1, z_2, \dots, z_L`.

    EXAMPLES:

    A BBS pseudorandom bit sequence with a specified seed::

        sage: from sage.crypto.stream import blum_blum_shub
        sage: blum_blum_shub(length=6, seed=3, p=11, q=19)
        110000

    You could specify the length of the bit string, with given values for
    ``p`` and ``q``::

        sage: blum_blum_shub(length=6, p=11, q=19)  # random
        001011

    Or you could specify the length of the bit string, with given values for
    the lower and upper bounds::

        sage: blum_blum_shub(length=6, lbound=10**4, ubound=10**5)  # random
        110111

    Under some reasonable hypotheses, Blum-Blum-Shub [BlumBlumShub1982]_
    sketch a proof that the period of the BBS stream cipher is equal to
    `\lambda(\lambda(n))`, where `\lambda(n)` is the Carmichael function of
    `n`. This is verified below in a few examples by using the function
    :func:`lfsr_connection_polynomial() <sage.crypto.lfsr.lfsr_connection_polynomial>`
    (written by Tim Brock) which computes the connection polynomial of a
    linear feedback shift register sequence. The degree of that polynomial
    is the period. ::

        sage: from sage.crypto.stream import blum_blum_shub
        sage: from sage.crypto.util import carmichael_lambda
        sage: carmichael_lambda(carmichael_lambda(7*11))
        4
        sage: s = [GF(2)(int(str(x))) for x in blum_blum_shub(60, p=7, q=11, seed=13)]
        sage: lfsr_connection_polynomial(s)
        x^3 + x^2 + x + 1
        sage: carmichael_lambda(carmichael_lambda(11*23))
        20
        sage: s = [GF(2)(int(str(x))) for x in blum_blum_shub(60, p=11, q=23, seed=13)]
        sage: lfsr_connection_polynomial(s)
        x^19 + x^18 + x^17 + x^16 + x^15 + x^14 + x^13 + x^12 + x^11 + x^10 + x^9 + x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1

    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.stream import blum_blum_shub
        sage: blum_blum_shub(6, lbound=24, ubound=30, ntries=10)
        Traceback (most recent call last):
        ...
        ValueError: No Blum primes within the specified closed interval.

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

        sage: blum_blum_shub(6, lbound=2, ubound=3)
        Traceback (most recent call last):
        ...
        ValueError: Both the lower and upper bounds must be > 2.
        sage: blum_blum_shub(6, lbound=3, ubound=2)
        Traceback (most recent call last):
        ...
        ValueError: Both the lower and upper bounds must be > 2.
        sage: blum_blum_shub(6, lbound=2, ubound=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: blum_blum_shub(6, lbound=3, ubound=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: blum_blum_shub(6, lbound=4, ubound=3)
        Traceback (most recent call last):
        ...
        ValueError: The lower bound must be less than the upper bound.

    REFERENCES:

    .. [BlumBlumShub1982] L. Blum, M. Blum, and M. Shub.
      Comparison of Two Pseudo-Random Number Generators.
      *Advances in Cryptology: Proceedings of Crypto '82*,
      pp.61--78, 1982.

    .. [BlumBlumShub1986] L. Blum, M. Blum, and M. Shub.
      A Simple Unpredictable Pseudo-Random Number Generator.
      *SIAM Journal on Computing*, 15(2):364--383, 1986.
    """
    # sanity checks
    if length < 0:
        raise ValueError("The length of the bit string must be positive.")
    if (p is None) and (p == q == lbound == ubound):
        raise ValueError(
            "Either specify values for p and q, or specify values for the lower and upper bounds."
        )
    # Use pre-computed Blum primes. Both the parameters p and q are
    # assumed to be Blum primes. No attempts are made to ensure that they
    # are indeed Blum primes.
    randp = 0
    randq = 0
    if (p is not None) and (q is not None):
        randp = p
        randq = q
    # generate random Blum primes within specified bounds
    elif (lbound is not None) and (ubound is not None):
        randp = random_blum_prime(lbound, ubound, ntries=ntries)
        randq = random_blum_prime(lbound, ubound, ntries=ntries)
        while randp == randq:
            randq = random_blum_prime(lbound, ubound, ntries=ntries)
    # no pre-computed primes given, and no appropriate bounds given
    else:
        raise ValueError(
            "Either specify values for p and q, or specify values for the lower and upper bounds."
        )
    # By now, we should have two distinct Blum primes.
    n = randp * randq
    # If no seed is provided, select a random seed.
    x0 = seed
    if seed is None:
        zmod = IntegerModRing(n)
        s = zmod.random_element().lift()
        while gcd(s, n) != 1:
            s = zmod.random_element().lift()
        x0 = power_mod(s, 2, n)
    # start generating pseudorandom bits
    z = []
    for i in xrange(length):
        x1 = power_mod(x0, 2, n)
        z.append(x1 % 2)
        x0 = x1
    bin = BinaryStrings()
    return bin(z)