Example #1
0
def discrete_log_lambda(a, base, bounds, operation='*', hash_function=hash):
    """
    Pollard Lambda algorithm for computing discrete logarithms. It uses
    only a logarithmic amount of memory. It's useful if you have
    bounds on the logarithm. If you are computing logarithms in a
    whole finite group, you should use Pollard Rho algorithm.

    INPUT:

    - a - a group element
    - base - a group element
    - bounds - a couple (lb,ub) representing the range where we look for a logarithm
    - operation - string: '+', '*' or 'other'
    - hash_function -- having an efficient hash function is critical for this algorithm

    OUTPUT: Returns an integer `n` such that `a=base^n` (or `a=n*base`)

    ALGORITHM: Pollard Lambda, if bounds are (lb,ub) it has time complexity
        O(sqrt(ub-lb)) and space complexity O(log(ub-lb))

    EXAMPLES::

        sage: F.<a> = GF(2^63)
        sage: discrete_log_lambda(a^1234567, a, (1200000,1250000))
        1234567

        sage: F.<a> = GF(37^5)
        sage: E = EllipticCurve(F, [1,1])
        sage: P = E.lift_x(a); P
        (a : 9*a^4 + 22*a^3 + 23*a^2 + 30 : 1)

    This will return a multiple of the order of P::

        sage: discrete_log_lambda(P.parent()(0), P, Hasse_bounds(F.order()), operation='+')
        69327408

        sage: K.<a> = GF(89**5)
        sage: hs = lambda x: hash(x) + 15
        sage: discrete_log_lambda(a**(89**3 - 3), a, (89**2, 89**4), operation = '*', hash_function = hs)  # long time (10s on sage.math, 2011)
        704966

    AUTHOR:

        -- Yann Laigle-Chapuy (2009-01-25)

    """
    from sage.rings.integer import Integer
    from operator import mul, add, pow

    if operation in addition_names:
        mult=add
        power=mul
    elif operation in multiplication_names:
        mult=mul
        power=pow
    else:
        raise ValueError("unknown operation")

    lb,ub = bounds
    if lb<0 or ub<lb:
        raise ValueError("discrete_log_lambda() requires 0<=lb<=ub")

    # check for mutability
    mut = hasattr(base,'set_immutable')

    width = Integer(ub-lb)
    N = width.isqrt()+1

    M = dict()
    for s in xrange(10): #to avoid infinite loops
        #random walk function setup
        k = 0
        while (2**k<N):
            r = sage.misc.prandom.randrange(1,N)
            M[k] = (r , power(base,r))
            k += 1
        #first random walk
        H = power(base,ub)
        c = ub
        for i in xrange(N):
            if mut: H.set_immutable()
            r,e = M[hash_function(H)%k]
            H = mult(H,e)
            c += r
        if mut: H.set_immutable()
        mem=set([H])
        #second random walk
        H = a
        d=0
        while c-d >= lb:
            if mut: H.set_immutable()
            if ub > c-d and H in mem:
                return c-d
            r,e = M[hash_function(H)%k]
            H = mult(H,e)
            d += r

    raise ValueError("Pollard Lambda failed to find a log")
Example #2
0
def discrete_log_lambda(a, base, bounds, operation='*', hash_function=hash):
    """
    Pollard Lambda algorithm for computing discrete logarithms. It uses
    only a logarithmic amount of memory. It's useful if you have
    bounds on the logarithm. If you are computing logarithms in a
    whole finite group, you should use Pollard Rho algorithm.

    INPUT:

    - a - a group element
    - base - a group element
    - bounds - a couple (lb,ub) representing the range where we look for a logarithm
    - operation - string: '+', '*' or 'other'
    - hash_function -- having an efficient hash function is critical for this algorithm

    OUTPUT: Returns an integer `n` such that `a=base^n` (or `a=n*base`)

    ALGORITHM: Pollard Lambda, if bounds are (lb,ub) it has time complexity
        O(sqrt(ub-lb)) and space complexity O(log(ub-lb))

    EXEMPLES::

        sage: F.<a> = GF(2^63)
        sage: discrete_log_lambda(a^1234567, a, (1200000,1250000))
        1234567

        sage: F.<a> = GF(37^5)
        sage: E = EllipticCurve(F, [1,1])
        sage: P = E.lift_x(a); P
        (a : 28*a^4 + 15*a^3 + 14*a^2 + 7 : 1)  # 32-bit
        (a : 9*a^4 + 22*a^3 + 23*a^2 + 30 : 1)  # 64-bit

    This will return a multiple of the order of P::

        sage: discrete_log_lambda(P.parent()(0), P, Hasse_bounds(F.order()), operation='+')
        69327408

        sage: K.<a> = GF(89**5)
        sage: hs = lambda x: hash(x) + 15
        sage: discrete_log_lambda(a**(89**3 - 3), a, (89**2, 89**4), operation = '*', hash_function = hs)  # long time (10s on sage.math, 2011)
        704966

    AUTHOR:

        -- Yann Laigle-Chapuy (2009-01-25)

    """
    from sage.rings.integer import Integer
    from operator import mul, add, pow

    if operation in addition_names:
        mult = add
        power = mul
    elif operation in multiplication_names:
        mult = mul
        power = pow
    else:
        raise ValueError("unknown operation")

    lb, ub = bounds
    if lb < 0 or ub < lb:
        raise ValueError("discrete_log_lambda() requires 0<=lb<=ub")

    # check for mutability
    mut = hasattr(base, 'set_immutable')

    width = Integer(ub - lb)
    N = width.isqrt() + 1

    M = dict()
    for s in xrange(10):  #to avoid infinite loops
        #random walk function setup
        k = 0
        while (2**k < N):
            r = sage.misc.prandom.randrange(1, N)
            M[k] = (r, power(base, r))
            k += 1
        #first random walk
        H = power(base, ub)
        c = ub
        for i in xrange(N):
            if mut: H.set_immutable()
            r, e = M[hash_function(H) % k]
            H = mult(H, e)
            c += r
        if mut: H.set_immutable()
        mem = set([H])
        #second random walk
        H = a
        d = 0
        while c - d >= lb:
            if mut: H.set_immutable()
            if ub > c - d and H in mem:
                return c - d
            r, e = M[hash_function(H) % k]
            H = mult(H, e)
            d += r

    raise ValueError("Pollard Lambda failed to find a log")
Example #3
0
def discrete_log_rho(a, base, ord=None, operation='*', hash_function=hash):
    """
    Pollard Rho algorithm for computing discrete logarithm in cyclic
    group of prime order.
    If the group order is very small it falls back to the baby step giant step
    algorithm.

    INPUT:

    - ``a`` -- a group element
    - ``base`` -- a group element
    - ``ord`` -- the order of ``base`` or ``None``, in this case we try
      to compute it
    - ``operation`` -- a string (default: ``'*'``) denoting whether we
      are in an additive group or a multiplicative one
    - ``hash_function`` -- having an efficient hash function is critical
      for this algorithm (see examples)

    OUTPUT: an integer `n` such that `a = base^n` (or `a = n*base`)

    ALGORITHM: Pollard rho for discrete logarithm, adapted from the
    article of Edlyn Teske, 'A space efficient algorithm for group
    structure computation'.

    EXAMPLES::

        sage: F.<a> = GF(2^13)
        sage: g = F.gen()
        sage: discrete_log_rho(g^1234, g)
        1234

        sage: F.<a> = GF(37^5)
        sage: E = EllipticCurve(F, [1,1])
        sage: G = (3*31*2^4)*E.lift_x(a)
        sage: discrete_log_rho(12345*G, G, ord=46591, operation='+')
        12345

    It also works with matrices::

        sage: A = matrix(GF(50021),[[10577,23999,28893],[14601,41019,30188],[3081,736,27092]])
        sage: discrete_log_rho(A^1234567, A)
        1234567

    Beware, the order must be prime::

        sage: I = IntegerModRing(171980)
        sage: discrete_log_rho(I(2), I(3))
        Traceback (most recent call last):
        ...
        ValueError: for Pollard rho algorithm the order of the group must be prime

    If it fails to find a suitable logarithm, it raises a ``ValueError``::

        sage: I = IntegerModRing(171980)
        sage: discrete_log_rho(I(31002),I(15501))
        Traceback (most recent call last):
        ...
        ValueError: Pollard rho algorithm failed to find a logarithm

    The main limitation on the hash function is that we don't want to have
    `hash(x*y) = hash(x) + hash(y)`::

        sage: I = IntegerModRing(next_prime(2^23))
        sage: def test():
        ....:     try:
        ....:          discrete_log_rho(I(123456),I(1),operation='+')
        ....:     except Exception:
        ....:          print("FAILURE")
        sage: test()  # random failure
        FAILURE

    If this happens, we can provide a better hash function::

        sage: discrete_log_rho(I(123456),I(1),operation='+', hash_function=lambda x: hash(x*x))
        123456

    AUTHOR:

    - Yann Laigle-Chapuy (2009-09-05)

    """
    from sage.rings.integer import Integer
    from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
    from operator import mul, add, pow

    # should be reasonable choices
    partition_size = 20
    memory_size = 4

    if operation in addition_names:
        mult = add
        power = mul
        if ord is None:
            ord = base.additive_order()
    elif operation in multiplication_names:
        mult = mul
        power = pow
        if ord is None:
            ord = base.multiplicative_order()
    else:
        raise ValueError

    ord = Integer(ord)

    if not ord.is_prime():
        raise ValueError("for Pollard rho algorithm the order of the group must be prime")

    # check if we need to set immutable before hashing
    mut = hasattr(base,'set_immutable')

    isqrtord=ord.isqrt()

    if isqrtord < partition_size: #setup to costly, use bsgs
        return bsgs(base,a, bounds=(0,ord), operation=operation)

    reset_bound = 8*isqrtord # we take some margin

    I=IntegerModRing(ord)

    for s in xrange(10): # to avoid infinite loops
        # random walk function setup
        m=[I.random_element() for i in xrange(partition_size)]
        n=[I.random_element() for i in xrange(partition_size)]
        M=[mult(power(base,Integer(m[i])),power(a,Integer(n[i]))) for i in xrange(partition_size)]

        ax = I.random_element()
        x = power(base,Integer(ax))
        if mut:
            x.set_immutable()

        bx = I(0)

        sigma=[(0,None)]*memory_size
        H={} # memory
        i0=0
        nextsigma = 0
        for i in xrange(reset_bound):
                    #random walk, we need an efficient hash
            s=hash_function(x) % partition_size
            (x,ax,bx) = (mult(M[s],x), ax+m[s], bx+n[s])
            if mut:
                x.set_immutable()
            # look for collisions
            if x in H:
                ay,by=H[x]
                if bx == by:
                    break
                else:
                    res = sage.rings.integer.Integer((ay-ax)/(bx-by))
                    if power(base,res) == a:
                        return res
                    else:
                        break
            # should we remember this value?
            elif i >= nextsigma:
                if sigma[i0][1] is not None:
                    H.pop(sigma[i0][1])
                sigma[i0]=(i,x)
                i0 = (i0+1) % memory_size
                nextsigma = 3*sigma[i0][0] #3 seems a good choice
                H[x]=(ax,bx)

    raise ValueError("Pollard rho algorithm failed to find a logarithm")
Example #4
0
def discrete_log_rho(a, base, ord=None, operation='*', hash_function=hash):
    """
    Pollard Rho algorithm for computing discrete logarithm in cyclic
    group of prime order.
    If the group order is very small it falls back to the baby step giant step
    algorithm.

    INPUT:

    - ``a`` -- a group element
    - ``base`` -- a group element
    - ``ord`` -- the order of ``base`` or ``None``, in this case we try
      to compute it
    - ``operation`` -- a string (default: ``'*'``) denoting whether we
      are in an additive group or a multiplicative one
    - ``hash_function`` -- having an efficient hash function is critical
      for this algorithm (see examples)

    OUTPUT: an integer `n` such that `a = base^n` (or `a = n*base`)

    ALGORITHM: Pollard rho for discrete logarithm, adapted from the
    article of Edlyn Teske, 'A space efficient algorithm for group
    structure computation'.

    EXAMPLES::

        sage: F.<a> = GF(2^13)
        sage: g = F.gen()
        sage: discrete_log_rho(g^1234, g)
        1234

        sage: F.<a> = GF(37^5)
        sage: E = EllipticCurve(F, [1,1])
        sage: G = (3*31*2^4)*E.lift_x(a)
        sage: discrete_log_rho(12345*G, G, ord=46591, operation='+')
        12345

    It also works with matrices::

        sage: A = matrix(GF(50021),[[10577,23999,28893],[14601,41019,30188],[3081,736,27092]])
        sage: discrete_log_rho(A^1234567, A)
        1234567

    Beware, the order must be prime::

        sage: I = IntegerModRing(171980)
        sage: discrete_log_rho(I(2), I(3))
        Traceback (most recent call last):
        ...
        ValueError: for Pollard rho algorithm the order of the group must be prime

    If it fails to find a suitable logarithm, it raises a ``ValueError``::

        sage: I = IntegerModRing(171980)
        sage: discrete_log_rho(I(31002),I(15501))
        Traceback (most recent call last):
        ...
        ValueError: Pollard rho algorithm failed to find a logarithm

    The main limitation on the hash function is that we don't want to have
    `hash(x*y) = hash(x) + hash(y)`::

        sage: I = IntegerModRing(next_prime(2^23))
        sage: def test():
        ....:     try:
        ....:          discrete_log_rho(I(123456),I(1),operation='+')
        ....:     except Exception:
        ....:          print "FAILURE"
        sage: test()  # random failure
        FAILURE

    If this happens, we can provide a better hash function::

        sage: discrete_log_rho(I(123456),I(1),operation='+', hash_function=lambda x: hash(x*x))
        123456

    AUTHOR:

    - Yann Laigle-Chapuy (2009-09-05)

    """
    from sage.rings.integer import Integer
    from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
    from operator import mul, add, pow

    # should be reasonable choices
    partition_size = 20
    memory_size = 4

    if operation in addition_names:
        mult = add
        power = mul
        if ord is None:
            ord = base.additive_order()
    elif operation in multiplication_names:
        mult = mul
        power = pow
        if ord is None:
            ord = base.multiplicative_order()
    else:
        raise ValueError

    ord = Integer(ord)

    if not ord.is_prime():
        raise ValueError(
            "for Pollard rho algorithm the order of the group must be prime")

    # check if we need to set immutable before hashing
    mut = hasattr(base, 'set_immutable')

    isqrtord = ord.isqrt()

    if isqrtord < partition_size:  #setup to costly, use bsgs
        return bsgs(base, a, bounds=(0, ord), operation=operation)

    reset_bound = 8 * isqrtord  # we take some margin

    I = IntegerModRing(ord)

    for s in xrange(10):  # to avoid infinite loops
        # random walk function setup
        m = [I.random_element() for i in xrange(partition_size)]
        n = [I.random_element() for i in xrange(partition_size)]
        M = [
            mult(power(base, Integer(m[i])), power(a, Integer(n[i])))
            for i in xrange(partition_size)
        ]

        ax = I.random_element()
        x = power(base, Integer(ax))
        if mut:
            x.set_immutable()

        bx = I(0)

        sigma = [(0, None)] * memory_size
        H = {}  # memory
        i0 = 0
        nextsigma = 0
        for i in xrange(reset_bound):
            #random walk, we need an efficient hash
            s = hash_function(x) % partition_size
            (x, ax, bx) = (mult(M[s], x), ax + m[s], bx + n[s])
            if mut:
                x.set_immutable()
            # look for collisions
            if x in H:
                ay, by = H[x]
                if bx == by:
                    break
                else:
                    res = sage.rings.integer.Integer((ay - ax) / (bx - by))
                    if power(base, res) == a:
                        return res
                    else:
                        break
            # should we remember this value?
            elif i >= nextsigma:
                if sigma[i0][1] is not None:
                    H.pop(sigma[i0][1])
                sigma[i0] = (i, x)
                i0 = (i0 + 1) % memory_size
                nextsigma = 3 * sigma[i0][0]  #3 seems a good choice
                H[x] = (ax, bx)

    raise ValueError("Pollard rho algorithm failed to find a logarithm")