Example #1
0
    def euclid2d(m, n, P, Q, PQ, A):
        """ The 2-dimensional scalar pseudomultiplication: x([r]P + [s - r]P) with r = s / {Golden Ratio}') """

        s0, s1 = m, n
        x0  = list(P)
        x1  = list(Q)
        diff= list(PQ)

        while s0 != 0:
            if s1 < s0:
                x0, x1 = cswap(x0, x1, 1)
                s0, s1 = s1, s0
            if s1 <= 4*s0:
                # Fibonacci step
                x   = list(x0)
                x0  = xadd(x1, x0, diff)
                diff= list(x)
                s1 -= s0
            elif (s0 % 2) == (s1 % 2):
                x0 = xadd(x1, x0, diff)
                x1 = xdbl(x1, A)
                s1 = (s1 - s0) // 2
            elif (s1 % 2) == 0:
                diff=xadd(x1, diff, x0)
                x1  =xdbl(x1, A)
                s1 //= 2
            else:
                diff= xadd(x0, diff, x1)
                x0  = xdbl(x0, A)
                s0 //= 2

        while s1 % 2 == 0:
            x1 = xdbl(x1, A)
            s1 //= 2

        if s1 > 1:
            # Ladder step on the missing part: x0 will correspond with Ladder(x1)
            diff= list(x1)
            x0  = xdbl(x1, A)
            s1_binary = bin(s1)[2:][::-1]
            s1_length = len(s1_binary)
            for i in range(s1_length - 2, -1, -1):
                x0, x1 = cswap(x0, x1, int(s1_binary[i + 1]) ^ int(s1_binary[i]))
                x1 = xadd(x0, x1, diff)
                x0 = xdbl(x0, A)

            x0, x1 = cswap(x0, x1, int(s1_binary[0]))
        else:
            # In this case, the output should correspond with x1, thus we swap to x0
            x0, x1 = cswap(x0, x1, 1)

        return x0
Example #2
0
 def Ladder3pt(m, P, Q, PQ, A):
     """
     ----------------------------------------------------------------------
     Ladder3pt()
     input : a projective Montgomery x-coordinate point x(P) := XP/ZP,
             x(Q) := XQ/ZQ, and x(P-Q) := XPQ/ZPQ, the affine Montgomery
             constant A where E : y^2 = x^3 + Ax^2 + x, and a positive
             integer m
     output: the projective Montgomery x-coordinate point x(P + [m]Q)
     ----------------------------------------------------------------------
     """
     X0 = list([field(Q[0]), field(Q[1])])
     X1 = list([field(P[0]), field(P[1])])
     X2 = list([field(PQ[0]), field(PQ[1])])
     t = 0x1
     for i in range(0, bitlength(p), 1):
         X1, X2 = cswap(X1, X2, t & m == 0)
         X0, X1 = xdbladd(X0, X1, X2, A)
         X1, X2 = cswap(X1, X2, t & m == 0)
         t <<= 1
     return X1
Example #3
0
    def elligator(A):
        """ elligator() samples two points on E[pi + 1] or E[pi - 1] """
        Ap = (A[0] + A[0])
        Ap = (Ap - A[1])
        Ap = (Ap + Ap)
        Cp = A[1]

        u = field(random.randint(2, p_minus_one_halves))
        u_squared = (u ** 2)

        u_squared_plus_one = (u_squared + 1)
        u_squared_minus_one = (u_squared - 1)

        C_times_u_squared_minus_one = (Cp * u_squared_minus_one)
        AC_times_u_squared_minus_one = (Ap * C_times_u_squared_minus_one)

        tmp = (Ap ** 2)
        tmp = (tmp * u_squared)
        aux = (C_times_u_squared_minus_one ** 2)
        tmp = (tmp + aux)
        tmp = (AC_times_u_squared_minus_one * tmp)

        alpha, beta = 0, u
        alpha, beta = cswap(alpha, beta, tmp == 0)
        u_squared_plus_one = (alpha * u_squared_plus_one)
        alpha = (alpha * C_times_u_squared_minus_one)

        Tp_X = (Ap + alpha)
        Tm_X = (Ap * u_squared)
        Tm_X = (Tm_X + alpha)
        Tm_X = (0 - Tm_X)

        tmp = (tmp + u_squared_plus_one)
        Tp_X, Tm_X = cswap(Tp_X, Tm_X, not tmp.issquare())

        return (
            [Tp_X, C_times_u_squared_minus_one],
            [Tm_X, C_times_u_squared_minus_one],
        )
Example #4
0
    def evaluate_strategy(self, E, P, L, strategy, n, m, e):
        """
        evaluate_strategy():
        inputs : a projective Montgomery constants A24:= A + 2C and C24:=4C where E : y^2 = x^3 + (A/C)*x^2 + x,
                 a list of two projective Montgomery x-coordinate torsion-(l_1 x ... l_n) points x(T_+) := XP/ZP
                 and x(T_-) := XM/ZM, the list of small odd primes [l_1, l_2, ..., l_n], an strategy and length
                 of the given list of small odd primes, maximum number of degree-l_i isogeny constructions, and
                 the secret integer vector to be evaluated
        output : the projective Montgomery constants a24:= a + 2c and c24:=4c where E': y^2 = x^3 + (a/c)*x^2 + x
                 is E / <P>, the new maximum and current number of degree-l_i isogeny constructions to be performed
                 after the strategy evaluation.

        NOTE: T_+ belongs to E[pi - 1] and T_- belongs to E[pi + 1]. In particular, P = [T_-, T_+]
        """
        v = list(m)
        u = list(e)
        ramifications = []
        moves = [
            0
        ]  # moves: this list determines whether an isogeny construction must be performed
        k = 0  # k: current element of the strategy

        ramifications.append(P)
        E_i = list(E)
        for i in range(len(strategy)):

            pos = self.formula.L.index(
                L[n - 1 -
                  i])  # Current element of self.formula.L to be required

            # Reaching the vertex (n - 1 - i, i)

            # Vertical edges
            prev = sum(moves)
            while prev < (n - 1 - i):

                moves.append(
                    strategy[k])  # Number of vertical edges to be performed
                T = list(ramifications[-1])  # New ramification
                for j in range(prev, prev + strategy[k], 1):
                    T = self.curve.xmul(T, E_i, self.formula.L.index(L[j]))

                ramifications.append(T)
                prev += strategy[k]
                k += 1

            # At this point, vertex (n - 1 - i, i) has been reached
            if (v[pos] > 0
                ):  # Maximum number of degree-l_{pos} isogeny constructions?

                # At this step, ramifications[-1] is the i-th leaf
                if self.curve.isinfinity(ramifications[-1]) == False:

                    # Dummy or NOT Dummy degree-(l_{n-1-i}) isogeny construction, that's the question?
                    b_i = isequal[u[pos] == 0]

                    ramifications[-1][0], ramifications[0][0] = cswap(
                        ramifications[-1][0], ramifications[0][0], b_i)
                    ramifications[-1][1], ramifications[0][1] = cswap(
                        ramifications[-1][1], ramifications[0][1], b_i)

                    if self.formula_name != 'tvelu':
                        # This branchs corresponds with the use of the new velu's formulaes

                        if self.tuned:
                            self.formula.set_parameters_velu(
                                self.formula.sJ_list[pos],
                                self.formula.sI_list[pos], pos)

                        else:
                            # -------------------------------------------------------------
                            # Parameters sJ and sI correspond with the parameters b and b' from example 4.12 of https://eprint.iacr.org/2020/341
                            # These paramters are required in KPs, xISOG, and xEVAL
                            if self.formula.L[pos] == 3:
                                b = 0
                                c = 0
                            else:
                                b = int(
                                    floor(sqrt(self.formula.L[pos] - 1) / 2.0))
                                c = int(
                                    floor((self.formula.L[pos] - 1.0) /
                                          (4.0 * b)))

                            self.formula.set_parameters_velu(b, c, pos)

                        if (self.formula_name == 'hvelu'
                                and self.formula.L[pos] <=
                                self.formula.HYBRID_BOUND):
                            K = self.formula.kps(ramifications[-1], E_i, pos)
                        else:
                            self.formula.kps(ramifications[-1], E_i, pos)
                            T = self.curve.xmul(ramifications[-1], E_i, pos)

                    else:
                        K = self.formula.kps(ramifications[-1], E_i, pos)

                    ramifications[-1][0], ramifications[0][0] = cswap(
                        ramifications[-1][0], ramifications[0][0], b_i)
                    ramifications[-1][1], ramifications[0][1] = cswap(
                        ramifications[-1][1], ramifications[0][1], b_i)

                    # New isogeny construction
                    C_i = self.formula.xisog(E_i, pos)

                    # Next, the horizontal edge [(0,i),(0,i+1)] is performed
                    if self.formula_name == 'tvelu' or (
                            self.formula_name == 'hvelu' and
                            self.formula.L[pos] <= self.formula.HYBRID_BOUND):
                        d_i = (self.formula.L[pos] - 1) // 2
                        mask = isequal[(self.formula.L[pos] == 3
                                        )]  # catching special case when l = 3

                        Z = self.formula.yadd(K[(d_i + mask) - 1], K[0],
                                              K[(d_i + mask) -
                                                2])  # y([d_i + 1]K[0])
                        Z[0], K[d_i][0] = cswap(Z[0], K[d_i][0], mask ^ 1)
                        Z[1], K[d_i][1] = cswap(Z[1], K[d_i][1], mask ^ 1)

                        T = self.formula.yadd(
                            K[d_i], K[d_i - 1],
                            K[0])  # y([2*d_i + 1]K[0]) := y([l_i]K[0])
                        T = [
                            (T[1] + T[0]),
                            (T[1] - T[0]),
                        ]  # x([l_i]K[0])

                    if self.formula_name == 'tvelu' or (
                            self.formula_name == 'hvelu' and
                            self.formula.L[pos] <= self.formula.HYBRID_BOUND):
                        ramifications[0] = self.formula.xeval(
                            ramifications[0], pos)
                    else:
                        ramifications[0] = self.formula.xeval(
                            ramifications[0], E_i)

                    T[0], ramifications[0][0] = cswap(T[0],
                                                      ramifications[0][0], b_i)
                    T[1], ramifications[0][1] = cswap(T[1],
                                                      ramifications[0][1], b_i)

                    # The remainder horizontal edges are performed
                    for j in range(1, len(moves) - 1, 1):

                        T = self.curve.xmul(ramifications[j], E_i, pos)

                        if self.formula_name == 'tvelu' or (
                                self.formula_name == 'hvelu'
                                and self.formula.L[pos] <=
                                self.formula.HYBRID_BOUND):
                            ramifications[j] = self.formula.xeval(
                                ramifications[j], pos)
                        else:
                            ramifications[j] = self.formula.xeval(
                                ramifications[j], E_i)

                        T[0], ramifications[j][0] = cswap(
                            T[0], ramifications[j][0], b_i)
                        T[1], ramifications[j][1] = cswap(
                            T[1], ramifications[j][1], b_i)

                    C_i[0], E_i[0] = cswap(C_i[0], E_i[0], b_i ^ 1)
                    C_i[1], E_i[1] = cswap(C_i[1], E_i[1], b_i ^ 1)

                    v[pos] -= 1
                    u[pos] -= b_i ^ 1
            else:
                for j in range(0, len(moves) - 1, 1):

                    ramifications[j] = self.curve.xmul(ramifications[j], E_i,
                                                       pos)

            moves.pop()
            ramifications.pop()

        pos = self.formula.L.index(
            L[0])  # Current element of self.formula.L to be required
        if self.curve.isinfinity(ramifications[0]) == False:

            if m[pos] > 0:

                b_i = isequal[e[pos] == 0]

                if self.formula_name != 'tvelu':
                    # This branchs corresponds with the use of the new velu's formulaes

                    if self.tuned:
                        self.formula.set_parameters_velu(
                            self.formula.sJ_list[pos],
                            self.formula.sI_list[pos], pos)

                    else:
                        # -------------------------------------------------------------
                        # Parameters sJ and sI correspond with the parameters b and b' from example 4.12 of https://eprint.iacr.org/2020/341
                        # These paramters are required in KPs, xISOG, and xEVAL
                        if self.formula.L[pos] == 3:
                            b = 0
                            c = 0
                        else:
                            b = int(floor(sqrt(self.formula.L[pos] - 1) / 2.0))
                            c = int(
                                floor((self.formula.L[pos] - 1.0) / (4.0 * b)))

                        self.formula.set_parameters_velu(b, c, pos)

                    self.formula.kps(ramifications[0], E_i, pos)

                else:
                    self.formula.kps(ramifications[0], E_i, pos)

                C_i = self.formula.xisog(E_i, pos)
                C_i[0], E_i[0] = cswap(C_i[0], E_i[0], b_i ^ 1)
                C_i[1], E_i[1] = cswap(C_i[1], E_i[1], b_i ^ 1)

                v[pos] -= 1
                u[pos] -= b_i ^ 1

        return E_i, v, u
Example #5
0
def bsidh_test(ctx):
    """ GF(p²)-operation cost of kps, xisog, and xeval """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    p = algo.params.p
    np = algo.params.np
    Ep = algo.params.Ep
    nm = algo.params.nm
    Em = algo.params.Em
    Lp = algo.params.Lp
    Lm = algo.params.Lm
    L = list(Lp + Lm)

    A = [algo.curve.field(8), algo.curve.field(4)]
    xmul = algo.curve.xmul
    isinfinity = algo.curve.isinfinity
    coeff = algo.curve.coeff
    isfullorder = algo.curve.isfullorder
    cofactor_multiples = algo.curve.cofactor_multiples
    Ladder3pt = algo.curve.Ladder3pt
    if algo.formula.name != 'tvelu':
        set_parameters_velu = algo.formula.set_parameters_velu
        print_parameters_velu = algo.formula.print_parameters_velu
        HYBRID_BOUND = algo.formula.HYBRID_BOUND

    init_runtime_field = algo.field.init_runtime
    show_runtime_field = algo.field.show_runtime
    init_runtime_basefield = algo.basefield.init_runtime
    show_runtime_basefield = algo.basefield.show_runtime
    kps = algo.formula.kps
    xisog = algo.formula.xisog
    xeval = algo.formula.xeval

    field = algo.field
    random = SystemRandom()

    print("p := 0x%X;" % p)
    print("fp := GF(p);")
    print("_<x> := PolynomialRing(fp);")
    print("fp2<u> := ext<fp | x^2 + 1>;")
    print("Pr<x> := PolynomialRing(fp2);")

    # ---Generators in E[p + 1]
    PA = list(algo.strategy.PA)
    QA = list(algo.strategy.QA)
    PQA = list(algo.strategy.PQA)
    # ---Generators in E[p - 1]
    PB = list(algo.strategy.PB)
    QB = list(algo.strategy.QB)
    PQB = list(algo.strategy.PQB)

    print("E := EllipticCurve(x^3 + (%s) * x^2 + x);" % coeff(A))

    S = list(PA)
    T = list(QA)
    ST = list(PQA)

    for i in range(0, np, 1):
        for idx in range(0, Ep[i], 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    assert isinfinity(S)
    assert isinfinity(T)
    assert isinfinity(ST)

    print("\n// Verifying torsion-(p + 1) points")
    print("// x([p + 1]PA) = (1:0)?\t", isinfinity(S))
    print("// x([p + 1]QA) = (1:0)?\t", isinfinity(T))
    print("// x([p + 1]PQA) = (1:0)?\t", isinfinity(ST))

    S = list(PB)
    T = list(QB)
    ST = list(PQB)

    for i in range(np, np + nm, 1):
        for idx in range(0, Em[i - np], 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    assert isinfinity(S)
    assert isinfinity(T)
    assert isinfinity(ST)
    print("\n// Verifying torsion-(p - 1) points")
    print("// x([p - 1]PB) = (1:0)?\t", isinfinity(S))
    print("// x([p - 1]QB) = (1:0)?\t", isinfinity(T))
    print("// x([p - 1]PQB) = (1:0)?\t", isinfinity(ST))

    # Case (p + 1)
    S = list(PA)
    T = list(QA)
    ST = list(PQA)

    assert isinfinity(S) == False
    assert isinfinity(T) == False
    assert isinfinity(ST) == False

    for i in range(0, np, 1):
        for idx in range(0, Ep[i] - 1, 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    print("\n// Verifying orders")
    assert isfullorder(cofactor_multiples(S, A, range(0, np, 1)))
    assert isfullorder(cofactor_multiples(T, A, range(0, np, 1)))
    assert isfullorder(cofactor_multiples(ST, A, range(0, np, 1)))
    print(
        "// PA is a full order point?\t",
        isfullorder(cofactor_multiples(S, A, range(0, np, 1))),
    )
    print(
        "// QA is a full order point?\t",
        isfullorder(cofactor_multiples(T, A, range(0, np, 1))),
    )
    print(
        "// QPA is a full order point?\t",
        isfullorder(cofactor_multiples(ST, A, range(0, np, 1))),
    )

    # Case (p - 1)
    S = list(PB)
    T = list(QB)
    ST = list(PQB)

    assert isinfinity(S) == False
    assert isinfinity(T) == False
    assert isinfinity(ST) == False

    for i in range(np, np + nm, 1):
        for idx in range(0, Em[i - np] - 1, 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    print("\n// Verifying orders")
    assert isfullorder(cofactor_multiples(S, A, range(np, np + nm, 1)))
    assert isfullorder(cofactor_multiples(T, A, range(np, np + nm, 1)))
    assert isfullorder(cofactor_multiples(ST, A, range(np, np + nm, 1)))
    print(
        "// PB is a full order point?\t",
        isfullorder(cofactor_multiples(S, A, range(np, np + nm, 1))),
    )
    print(
        "// QB is a full order point?\t",
        isfullorder(cofactor_multiples(T, A, range(np, np + nm, 1))),
    )
    print(
        "// QPB is a full order point?\t",
        isfullorder(cofactor_multiples(ST, A, range(np, np + nm, 1))),
    )

    # Three point ladder: case (p + 1)
    S = list(PA)
    T = list(QA)
    ST = list(PQA)

    assert isinfinity(S) == False
    assert isinfinity(T) == False

    for i in range(0, np, 1):
        for idx in range(0, Ep[i] - 1, 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    k = random.randint(0, p)
    R = Ladder3pt(k, S, T, ST, algo.curve.field(2))
    T_p = list(R)
    T_m = list(S)
    print(
        "\n// Now, we proceed by performing xisog with input curve equals the output curve of the previous one experiment, and using torsion-(p + 1) points"
    )
    for idx in range(0, np, 1):

        # -------------------------------------------------------------
        # Random kernel point
        Tp = list(T_p)
        for i in range(idx + 1, np, 1):
            Tp = xmul(Tp, A, i)

        print("// l:\t%7d |" % L[idx], end="")
        total_cost = [0, 0, 0]

        if setting.formula != 'tvelu':

            if setting.tuned:
                set_parameters_velu(algo.formula.sJ_list[idx],
                                    algo.formula.sI_list[idx], idx)

            else:
                # -------------------------------------------------------------
                # Parameters sJ and sI correspond with the parameters b and b'
                # from example 4.12 of https://eprint.iacr.org/2020/341 These
                # paramters are required in kps, xisog, and xeval
                if L[idx] <= 4:
                    b = 0
                    c = 0
                else:
                    b = int(floor(sqrt(L[idx] - 1) / 2.0))
                    c = int(floor((L[idx] - 1.0) / (4.0 * b)))

                set_parameters_velu(b, c, idx)

            print_parameters_velu()

        # -------------------------------------------------------------
        # kps procedure
        init_runtime_basefield()
        init_runtime_field()
        kps(Tp, A, idx)
        show_runtime_field("kps")

        total_cost[0] += algo.field.fp2mul
        total_cost[1] += algo.field.fp2sqr
        total_cost[2] += algo.field.fp2add

        # -------------------------------------------------------------
        # xisog
        init_runtime_basefield()
        init_runtime_field()
        Tp[0], A[0] = cswap(Tp[0], A[0], L[idx] == 4)
        Tp[1], A[1] = cswap(Tp[1], A[1], L[idx] == 4)
        B = xisog(A, idx)
        Tp[0], A[0] = cswap(Tp[0], A[0], L[idx] == 4)
        Tp[1], A[1] = cswap(Tp[1], A[1], L[idx] == 4)
        show_runtime_field("xisog")

        total_cost[0] += algo.field.fp2mul
        total_cost[1] += algo.field.fp2sqr
        total_cost[2] += algo.field.fp2add

        # -------------------------------------------------------------
        # xeval: kernel point determined by the next isogeny evaluation
        init_runtime_basefield()
        init_runtime_field()
        if (setting.formula == 'tvelu'
                or (setting.formula == 'hvelu' and L[idx] <= HYBRID_BOUND)
                or (L[idx] == 4)):
            T_p = xeval(T_p, idx)
        else:
            T_p = xeval(T_p, A)

        # xeval bench
        init_runtime_basefield()
        init_runtime_field()
        if (setting.formula == 'tvelu'
                or (setting.formula == 'hvelu' and L[idx] <= HYBRID_BOUND)
                or (L[idx] == 4)):
            T_m = xeval(T_m, idx)
        else:
            T_m = xeval(T_m, A)

        show_runtime_field("xeval")

        total_cost[0] += algo.field.fp2mul
        total_cost[1] += algo.field.fp2sqr
        total_cost[2] += algo.field.fp2add
        print("|| cost: %8d" % (total_cost[0] + total_cost[1]), end=" ")
        print("|| ratio: %1.3f" % ((total_cost[0] + total_cost[1]) /
                                   (L[idx] + 2.0)))

        # assert(validate(B))
        A = list(B)

        # print("B := EllipticCurve(x^3 + 0x%X * x^2 + x);" % coeff(A))
        # print("assert(Random(B) * (p + 1) eq B!0);")
        # print("BOOL, Q := IsPoint(B, fp!%d/%d);" % (T_m[0], T_m[1]))
        # print("assert(BOOL);")

    print(
        "\n// All the l_i's have been processed, output of xisog corresponds with the given below"
    )
    # print("B := EllipticCurve(x^3 + 0x%X * x^2 + x);" % coeff(A))
    print("B := EllipticCurve(x^3 + (%s) * x^2 + x);" % coeff(A))
    print("assert(Random(B) * (p + 1) eq B!0);")

    A = [algo.curve.field(8), algo.curve.field(4)]
    # Three point ladder: case (p - 1)
    S = list(PB)
    T = list(QB)
    ST = list(PQB)

    assert isinfinity(S) == False
    assert isinfinity(T) == False

    for i in range(np, np + nm, 1):
        for idx in range(0, Em[i - np] - 1, 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    k = random.randint(0, p)
    R = Ladder3pt(k, S, T, ST, algo.curve.field(2))
    T_p = list(R)
    T_m = list(S)
    print(
        "\n// Now, we proceed by performing xisog with input curve equals the output curve of the previous one experiment, and using torsion-(p - 1) points"
    )
    for idx in range(np, np + nm, 1):

        # -------------------------------------------------------------
        # Random kernel point
        Tp = list(T_p)
        for i in range(idx + 1, np + nm, 1):
            Tp = xmul(Tp, A, i)

        print("// l:\t%7d |" % L[idx], end="")
        total_cost = [0, 0, 0]

        if setting.formula != 'tvelu':

            if setting.tuned:
                set_parameters_velu(algo.formula.sJ_list[idx],
                                    algo.formula.sI_list[idx], idx)

            else:
                # -------------------------------------------------------------
                # Parameters sJ and sI correspond with the parameters b and b'
                # from example 4.12 of https://eprint.iacr.org/2020/341 These
                # paramters are required in kps, xisog, and xeval
                if L[idx] == 3:
                    b = 0
                    c = 0
                else:
                    b = int(floor(sqrt(L[idx] - 1) / 2.0))
                    c = int(floor((L[idx] - 1.0) / (4.0 * b)))

                set_parameters_velu(b, c, idx)

            print_parameters_velu()

        # -------------------------------------------------------------
        # kps procedure
        init_runtime_basefield()
        init_runtime_field()
        kps(Tp, A, idx)
        show_runtime_field("kps")

        total_cost[0] += algo.field.fp2mul
        total_cost[1] += algo.field.fp2sqr
        total_cost[2] += algo.field.fp2add

        # -------------------------------------------------------------
        # xisog
        init_runtime_basefield()
        init_runtime_field()
        B = xisog(A, idx)
        show_runtime_field("xisog")

        total_cost[0] += algo.field.fp2mul
        total_cost[1] += algo.field.fp2sqr
        total_cost[2] += algo.field.fp2add

        # -------------------------------------------------------------
        # xeval: kernel point determined by the next isogeny evaluation
        init_runtime_basefield()
        init_runtime_field()
        if setting.formula == 'tvelu' or (setting.formula == 'hvelu'
                                          and L[idx] <= HYBRID_BOUND):
            T_p = xeval(T_p, idx)
        else:
            T_p = xeval(T_p, A)

        # xeval bench
        init_runtime_basefield()
        init_runtime_field()
        if setting.formula == 'tvelu' or (setting.formula == 'hvelu'
                                          and L[idx] <= HYBRID_BOUND):
            T_m = xeval(T_m, idx)
        else:
            T_m = xeval(T_m, A)

        show_runtime_field("xeval")

        total_cost[0] += algo.field.fp2mul
        total_cost[1] += algo.field.fp2sqr
        total_cost[2] += algo.field.fp2add
        print("|| cost: %8d" % (total_cost[0] + total_cost[1]), end=" ")
        print("|| ratio: %1.3f" % ((total_cost[0] + total_cost[1]) /
                                   (L[idx] + 2.0)))

        # assert(validate(B))
        A = list(B)

        # print("B := EllipticCurve(x^3 + 0x%X * x^2 + x);" % coeff(A))
        # print("assert(Random(B) * (p + 1) eq B!0);")
        # print("BOOL, Q := IsPoint(B, fp!%d/%d);" % (T_m[0], T_m[1]))
        # print("assert(BOOL);")

    print(
        "\n// All the l_i's have been processed, output of xisog corresponds with the given below"
    )

    print("B := EllipticCurve(x^3 + (%s) * x^2 + x);" % coeff(A))
    print("assert(Random(B) * (p + 1) eq B!0);")
    # """

    print(
        "\n\"If no errors were showed using magma calculator, then all experiments were successfully passed!\";"
    )
    print("// copy and paste it at http://magma.maths.usyd.edu.au/calc/\n")
    return attrdict(name='bsidh-test', **locals())
Example #6
0
    def evaluate_strategy(self, EVAL, S_in, T_in, ST_in, E, P, L, strategy, n):
        '''
        evaluate_strategy():
                 primes;
        output : the projective Montgomery constants a24:= a + 2c and c24:=4c where E': y^2 = x^3 + (a/c)*x^2 + x
                     is E / <P>
        '''

        ramifications = []
        moves = [
            0
        ]  # moves: this list determines whether an isogeny construction must be performed
        k = 0  # k: current element of the strategy

        ramifications.append(list(P))
        E_i = list(E)

        if EVAL:
            # Public points to be evaluated
            S_out = list(
                S_in
            )  # x(S) should be a torsion point with not common factors in L
            T_out = list(
                T_in
            )  # x(T) should be a torsion point with not common factors in L
            ST_out = list(
                ST_in
            )  # x(S - T) should be a torsion point with not common factors in L
        else:
            S_out = None
            T_out = None
            ST_out = None

        assert len(strategy) == (n - 1)
        for i in range(len(strategy)):

            pos = self.L.index(
                L[n - 1 - i])  # Current element of self.L to be required

            # Reaching the vertex (n - 1 - i, i)
            # Vertical edges (scalar multiplications)
            prev = sum(moves)
            while prev < (n - 1 - i):

                moves.append(
                    strategy[k])  # Number of vertical edges to be performed
                T = list(ramifications[-1])  # New ramification
                for j in range(prev, prev + strategy[k], 1):
                    T = self.curve.xmul(T, E_i, self.L.index(L[j]))

                ramifications.append(list(T))
                prev += strategy[k]
                k += 1

            # Deciding which velu variant will be used
            if self.formula_name != 'tvelu':
                # This branchs corresponds with the use of the new velu's formulaes

                if self.tuned:
                    self.formula.set_parameters_velu(self.formula.sJ_list[pos],
                                                     self.formula.sI_list[pos],
                                                     pos)

                else:
                    # -------------------------------------------------------------
                    # Parameters sJ and sI correspond with the parameters b and b' from example 4.12 of https://eprint.iacr.org/2020/341
                    # These paramters are required in self.formula.kps, self.formula.xisog, and self.formula.xeval
                    if self.L[pos] <= 4:
                        b = 0
                        c = 0
                    else:
                        b = int(floor(sqrt(self.L[pos] - 1) / 2.0))
                        c = int(floor((self.L[pos] - 1.0) / (4.0 * b)))

                    if self.formula_name != 'tvelu':
                        self.formula.set_parameters_velu(b, c, pos)

            # Kernel Points computation
            self.formula.kps(ramifications[-1], E_i, pos)

            if not EVAL:
                # Isogeny construction
                ramifications[-1][0], E_i[0] = cswap(ramifications[-1][0],
                                                     E_i[0], self.L[pos] == 4)
                ramifications[-1][1], E_i[1] = cswap(ramifications[-1][1],
                                                     E_i[1], self.L[pos] == 4)
                C_i = self.formula.xisog(E_i, pos)
                ramifications[-1][0], E_i[0] = cswap(ramifications[-1][0],
                                                     E_i[0], self.L[pos] == 4)
                ramifications[-1][1], E_i[1] = cswap(ramifications[-1][1],
                                                     E_i[1], self.L[pos] == 4)

            # Now, we proceed by perform horizontal edges (isogeny evaluations)
            for j in range(0, len(moves) - 1, 1):

                if (self.formula_name == 'tvelu'
                        or (self.formula_name == 'hvelu'
                            and self.L[pos] <= self.formula.HYBRID_BOUND)
                        or (self.L[pos] == 4)):
                    ramifications[j] = self.formula.xeval(
                        ramifications[j], pos)
                else:
                    ramifications[j] = self.formula.xeval(
                        ramifications[j], E_i)

            if EVAL:
                # Evaluating public points
                if (self.formula_name == 'tvelu'
                        or (self.formula_name == 'hvelu'
                            and self.L[pos] <= self.formula.HYBRID_BOUND)
                        or (self.L[pos] == 4)):

                    S_out = self.formula.xeval(S_out, pos)
                    T_out = self.formula.xeval(T_out, pos)
                    ST_out = self.formula.xeval(ST_out, pos)
                else:

                    S_out = self.formula.xeval(S_out, E_i)
                    T_out = self.formula.xeval(T_out, E_i)
                    ST_out = self.formula.xeval(ST_out, E_i)

                C_i = self.curve.get_A(S_out, T_out, ST_out)

            # Updating the Montogmery curve coefficients
            E_i = [self.field(C_i[0]), self.field(C_i[1])]

            moves.pop()
            ramifications.pop()

        pos = self.L.index(L[0])  # Current element of self.L to be required

        if self.formula_name != 'tvelu':
            # This branchs corresponds with the use of the new velu's formulaes

            if self.tuned:
                self.formula.set_parameters_velu(self.formula.sJ_list[pos],
                                                 self.formula.sI_list[pos],
                                                 pos)

            else:
                # -------------------------------------------------------------
                # Parameters sJ and sI correspond with the parameters b and b' from example 4.12 of https://eprint.iacr.org/2020/341
                # These paramters are required in self.formula.kps, self.formula.xisog, and self.formula.xeval
                if self.L[pos] <= 4:
                    b = 0
                    c = 0
                else:
                    b = int(floor(sqrt(self.L[pos] - 1) / 2.0))
                    c = int(floor((self.L[pos] - 1.0) / (4.0 * b)))

                self.formula.set_parameters_velu(b, c, pos)

        # Kernel Points computations
        self.formula.kps(ramifications[0], E_i, pos)

        if not EVAL:
            # Isogeny construction
            ramifications[0][0], E_i[0] = cswap(ramifications[0][0], E_i[0],
                                                self.L[pos] == 4)
            ramifications[0][1], E_i[1] = cswap(ramifications[0][1], E_i[1],
                                                self.L[pos] == 4)
            C_i = self.formula.xisog(E_i, pos)
            ramifications[0][0], E_i[0] = cswap(ramifications[0][0], E_i[0],
                                                self.L[pos] == 4)
            ramifications[0][1], E_i[1] = cswap(ramifications[0][1], E_i[1],
                                                self.L[pos] == 4)

        else:

            # Evaluating public points
            if (self.formula_name == 'tvelu'
                    or (self.formula_name == 'hvelu'
                        and self.L[pos] <= self.formula.HYBRID_BOUND)
                    or (self.L[pos] == 4)):

                S_out = self.formula.xeval(S_out, pos)
                T_out = self.formula.xeval(T_out, pos)
                ST_out = self.formula.xeval(ST_out, pos)

            else:

                S_out = self.formula.xeval(S_out, E_i)
                T_out = self.formula.xeval(T_out, E_i)
                ST_out = self.formula.xeval(ST_out, E_i)

            C_i = self.curve.get_A(S_out, T_out, ST_out)

        # Updating the Montogmery curve coefficients
        E_i = [self.field(C_i[0]), self.field(C_i[1])]

        return E_i, S_out, T_out, ST_out
Example #7
0
def bsidh_precompute_parameters(ctx):
    """ Precomputation of tuned velusqrt parameters """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    p = algo.params.p
    np = algo.params.np
    Ep = algo.params.Ep
    nm = algo.params.nm
    Em = algo.params.Em
    Lp = algo.params.Lp
    Lm = algo.params.Lm
    L = list(Lp + Lm)

    A = [algo.curve.field(8), algo.curve.field(4)]
    xmul = algo.curve.xmul
    isinfinity = algo.curve.isinfinity
    coeff = algo.curve.coeff
    isfullorder = algo.curve.isfullorder
    cofactor_multiples = algo.curve.cofactor_multiples
    Ladder3pt = algo.curve.Ladder3pt
    if algo.formula.name != 'tvelu':
        set_parameters_velu = algo.formula.set_parameters_velu
        print_parameters_velu = algo.formula.print_parameters_velu
        HYBRID_BOUND = algo.formula.HYBRID_BOUND

    init_runtime_field = algo.field.init_runtime
    show_runtime_field = algo.field.show_runtime
    init_runtime_basefield = algo.basefield.init_runtime
    show_runtime_basefield = algo.basefield.show_runtime
    kps = algo.formula.kps
    xisog = algo.formula.xisog
    xeval = algo.formula.xeval

    field = algo.field
    random = SystemRandom()

    # ---Generators in E[p + 1]
    PA = list(algo.strategy.PA)
    QA = list(algo.strategy.QA)
    PQA = list(algo.strategy.PQA)
    # ---Generators in E[p - 1]
    PB = list(algo.strategy.PB)
    QB = list(algo.strategy.QB)
    PQB = list(algo.strategy.PQB)

    S = list(PA)
    T = list(QA)
    ST = list(PQA)

    for i in range(0, np, 1):
        for idx in range(0, Ep[i], 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    assert isinfinity(S)
    assert isinfinity(T)
    assert isinfinity(ST)

    S = list(PB)
    T = list(QB)
    ST = list(PQB)

    for i in range(np, np + nm, 1):
        for idx in range(0, Em[i - np], 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    assert isinfinity(S)
    assert isinfinity(T)
    assert isinfinity(ST)

    # Case (p + 1)
    S = list(PA)
    T = list(QA)
    ST = list(PQA)

    assert isinfinity(S) == False
    assert isinfinity(T) == False
    assert isinfinity(ST) == False

    for i in range(0, np, 1):
        for idx in range(0, Ep[i] - 1, 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    assert isfullorder(cofactor_multiples(S, A, range(0, np, 1)))
    assert isfullorder(cofactor_multiples(T, A, range(0, np, 1)))
    assert isfullorder(cofactor_multiples(ST, A, range(0, np, 1)))

    # Case (p - 1)
    S = list(PB)
    T = list(QB)
    ST = list(PQB)

    assert isinfinity(S) == False
    assert isinfinity(T) == False
    assert isinfinity(ST) == False

    for i in range(np, np + nm, 1):
        for idx in range(0, Em[i - np] - 1, 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    assert isfullorder(cofactor_multiples(S, A, range(np, np + nm, 1)))
    assert isfullorder(cofactor_multiples(T, A, range(np, np + nm, 1)))
    assert isfullorder(cofactor_multiples(ST, A, range(np, np + nm, 1)))

    # Three point ladder: case (p + 1)
    S = list(PA)
    T = list(QA)
    ST = list(PQA)

    assert isinfinity(S) == False
    assert isinfinity(T) == False

    for i in range(0, np, 1):
        for idx in range(0, Ep[i] - 1, 1):
            S = xmul(S, A, i)
            T = xmul(T, A, i)
            ST = xmul(ST, A, i)

    k = random.randint(0, p)
    R = Ladder3pt(k, S, T, ST, A)
    T_p = list(R)
    T_m = list(S)

    original_stdout = sys.stdout  # Save a reference to the original standard output
    multievaluation = {
        True: 'scaled',
        False: 'unscaled'
    }[setting.multievaluation]
    path = resource_filename(
        'sibc', "data/ijk/" + algo.curve.model + '/' + algo.curve.name + '-' +
        multievaluation)
    with open(path, 'w') as f:
        sys.stdout = f  # Change the standard output to the file we created.
        parameters = dict()
        for idx in range(0, np, 1):

            # -------------------------------------------------------------
            # Random kernel point
            Tp = list(T_p)
            for i in range(0, np, 1):
                if i != idx:
                    Tp = xmul(Tp, A, i)

            # -------------------------------------------------------------
            # Parameters sJ and sI correspond with the parameters b and b' from example 4.12 of https://eprint.iacr.org/2020/341
            # These paramters are required in kps, xisog, and xEVAL
            if L[idx] <= 4:
                b = 0
                c = 0
            else:
                b = int(floor(sqrt(L[idx] - 1) / 2.0))
                c = int(floor((L[idx] - 1.0) / (4.0 * b)))

            parameters[str(idx)] = []
            b += 1
            for j in range(0, int(floor(sqrt(pi * (b - 1)) / 1.0)), 1):

                b -= 1
                c = int(floor((L[idx] - 1.0) / (4.0 * b)))
                set_parameters_velu(b, c, idx)

                total_cost = [0, 0, 0]

                # -------------------------------------------------------------
                # kps procedure
                init_runtime_basefield()
                kps(Tp, A, idx)

                total_cost[0] += algo.basefield.fpmul
                total_cost[1] += algo.basefield.fpsqr
                total_cost[2] += algo.basefield.fpadd

                # -------------------------------------------------------------
                # xisog
                init_runtime_basefield()
                Tp[0], A[0] = cswap(Tp[0], A[0], L[idx] == 4)
                Tp[1], A[1] = cswap(Tp[1], A[1], L[idx] == 4)
                B = xisog(A, idx)
                Tp[0], A[0] = cswap(Tp[0], A[0], L[idx] == 4)
                Tp[1], A[1] = cswap(Tp[1], A[1], L[idx] == 4)

                total_cost[0] += algo.basefield.fpmul
                total_cost[1] += algo.basefield.fpsqr
                total_cost[2] += algo.basefield.fpadd

                # -------------------------------------------------------------
                # xEVAL bench
                init_runtime_basefield()
                if setting.formula == 'tvelu' or (setting.formula == 'hvelu'
                                                  and L[idx] <= HYBRID_BOUND):
                    Tm = xeval(T_m, idx)
                else:
                    Tm = xeval(T_m, A)

                total_cost[0] += algo.basefield.fpmul
                total_cost[1] += algo.basefield.fpsqr
                total_cost[2] += algo.basefield.fpadd

                parameters[str(idx)].append((b, c, [
                    algo.basefield.fpmul, algo.basefield.fpsqr,
                    algo.basefield.fpadd
                ], total_cost[0] + total_cost[1]))

            if L[idx] <= 4:
                parameters[str(idx)] = (0, 0, None, None)
            else:
                parameters[str(idx)] = min(parameters[str(idx)],
                                           key=lambda tup: tup[3])

            print(parameters[str(idx)][0], parameters[str(idx)][1])

        # --------------------------------------------------------------------------------------------------------------------
        A = [algo.curve.field(8), algo.curve.field(4)]
        # Three point ladder: case (p - 1)
        S = list(PB)
        T = list(QB)
        ST = list(PQB)

        assert isinfinity(S) == False
        assert isinfinity(T) == False

        for i in range(np, np + nm, 1):
            for idx in range(0, Em[i - np] - 1, 1):
                S = xmul(S, A, i)
                T = xmul(T, A, i)
                ST = xmul(ST, A, i)

        k = random.randint(0, p)
        R = Ladder3pt(k, S, T, ST, A)
        T_p = list(R)
        T_m = list(S)
        for idx in range(np, np + nm, 1):

            # -------------------------------------------------------------
            # Random kernel point
            Tp = list(T_p)
            for i in range(np, np + nm, 1):
                if i != idx:
                    Tp = xmul(Tp, A, i)

            # -------------------------------------------------------------
            # Parameters sJ and sI correspond with the parameters b and b' from example 4.12 of https://eprint.iacr.org/2020/341
            # These paramters are required in kps, xisog, and xEVAL
            if L[idx] == 3:
                b = 0
                c = 0
            else:
                b = int(floor(sqrt(L[idx] - 1) / 2.0))
                c = int(floor((L[idx] - 1.0) / (4.0 * b)))

            parameters[str(idx)] = []
            b += 1
            for j in range(0, int(floor(sqrt(pi * (b - 1)) / 1.0)), 1):

                b -= 1
                c = int(floor((L[idx] - 1.0) / (4.0 * b)))
                set_parameters_velu(b, c, idx)

                total_cost = [0, 0, 0]
                # -------------------------------------------------------------
                # kps procedure
                init_runtime_basefield()
                kps(Tp, A, idx)

                total_cost[0] += algo.basefield.fpmul
                total_cost[1] += algo.basefield.fpsqr
                total_cost[2] += algo.basefield.fpadd

                # -------------------------------------------------------------
                # xisog
                init_runtime_basefield()
                B = xisog(A, idx)

                total_cost[0] += algo.basefield.fpmul
                total_cost[1] += algo.basefield.fpsqr
                total_cost[2] += algo.basefield.fpadd

                # -------------------------------------------------------------
                # xEVAL bench
                init_runtime_basefield()
                if setting.formula == 'tvelu' or (setting.formula == 'hvelu'
                                                  and L[idx] <= HYBRID_BOUND):
                    Tm = xeval(T_m, idx)
                else:
                    Tm = xeval(T_m, A)

                total_cost[0] += algo.basefield.fpmul
                total_cost[1] += algo.basefield.fpsqr
                total_cost[2] += algo.basefield.fpadd

                parameters[str(idx)].append((b, c, [
                    algo.basefield.fpmul, algo.basefield.fpsqr,
                    algo.basefield.fpadd
                ], total_cost[0] + total_cost[1]))

            if L[idx] == 3:
                parameters[str(idx)] = (0, 0, None, None)
            else:
                parameters[str(idx)] = min(parameters[str(idx)],
                                           key=lambda tup: tup[3])

            print(parameters[str(idx)][0], parameters[str(idx)][1])
        sys.stdout = original_stdout  # Reset the standard output to its original value

    return attrdict(name='bsidh-precompute-parameters', **locals())
Example #8
0
    def evaluate_strategy(self, E, P, L, strategy, n, m, e):
        """
        evaluate_strategy():
        inputs : a projective Montgomery constants A24:= A + 2C and C24:=4C where E : y^2 = x^3 + (A/C)*x^2 + x,
                 a list of two projective Montgomery x-coordinate torsion-(l_1 x ... l_n) points x(T_+) := XP/ZP
                 and x(T_-) := XM/ZM, the list of small odd primes [l_1, l_2, ..., l_n], an strategy and length
                 of the given list of small odd primes, maximum number of degree-l_i isogeny constructions, and
                 the secret integer vector to be evaluated
        output : the projective Montgomery constants a24:= a + 2c and c24:=4c where E': y^2 = x^3 + (a/c)*x^2 + x
                 is E / <P>, the new maximum and current number of degree-l_i isogeny constructions to be performed
                 after the strategy evaluation.

        NOTE: T_+ belongs to E[pi - 1] and T_- belongs to E[pi + 1]. In particular, P = [T_-, T_+]
        """
        v = list(m)
        u = list(e)
        ramifications = []
        moves = [
            0
        ]  # moves: this list determines whether an isogeny construction must be performed
        k = 0  # k: current element of the strategy
        t = 1  # sign to be flip

        ramifications.append(
            list(P)
        )  # list of pair of points (T_-, T_+) such that T_- in E[\pi + 1] and T_+ in E[\pi - 1]
        E_i = list(E)
        for i in range(len(strategy)):

            pos = self.formula.L.index(
                L[n - 1 - i]
            )  # Current element of self.formula.L to be required

            # Reaching the vertex (n - 1 - i, i)

            # Vertical edges
            prev = sum(moves)
            while (prev + strategy[k]) < (n - 1 - i):

                moves.append(
                    strategy[k]
                )  # Number of vertical edges to be performed
                T = list(
                    [list(ramifications[-1][0]), list(ramifications[-1][1])]
                )
                for j in range(prev, prev + strategy[k], 1):
                    T = list(
                        [
                            self.curve.xmul(
                                T[0], E_i, self.formula.L.index(L[j])
                            ),
                            self.curve.xmul(
                                T[1], E_i, self.formula.L.index(L[j])
                            ),
                        ]
                    )

                ramifications.append(list([list(T[0]), list(T[1])]))
                prev += strategy[k]
                k += 1

            # Vertical edges without ramifications
            s_i = sign(e[pos])  # Sign of e[pos]
            c_i = (s_i + 1) // 2  # Constant-swap of T_+ and T_-

            if prev < (n - 1 - i):

                moves.append(
                    strategy[k]
                )  # Number of vertical edges (without ramifications) to be performed
                T = list(
                    [list(ramifications[-1][0]), list(ramifications[-1][1])]
                )

                T[0][0], T[1][0] = cswap(T[0][0], T[1][0], c_i)
                T[0][1], T[1][1] = cswap(T[0][1], T[1][1], c_i)
                for j in range(prev, prev + strategy[k], 1):
                    T[0] = list(
                        self.curve.xmul(
                            T[0], E_i, self.formula.L.index(L[j])
                        )
                    )  # A single scalar multiplication is required

                T[0][0], T[1][0] = cswap(T[0][0], T[1][0], c_i)
                T[0][1], T[1][1] = cswap(T[0][1], T[1][1], c_i)

                ramifications.append(list([list(T[0]), list(T[1])]))
                prev += strategy[k]
                k += 1

            # At this point, vertex (n - 1 - i, i) has been reached
            if (
                v[pos] > 0
            ):  # Maximum number of degree-l_{pos} isogeny constructions?

                # At this step, ramifications[-1] is the i-th leaf
                # Swap the torsion-(l_{n-j}) elliptic curve points on the current leaf
                #                           T_- <- ramifications[-1][0]
                #                           T_+ <- ramifications[-1][1]
                (
                    ramifications[-1][0][0],
                    ramifications[-1][1][0],
                ) = cswap(
                    ramifications[-1][0][0], ramifications[-1][1][0], c_i
                )  # XT_+ <-> XT_-
                (
                    ramifications[-1][0][1],
                    ramifications[-1][1][1],
                ) = cswap(
                    ramifications[-1][0][1], ramifications[-1][1][1], c_i
                )  # ZT_+ <-> ZT_-

                if self.curve.isinfinity(ramifications[-1][0]) == False:

                    if self.formula_name != 'tvelu':
                        # This branchs corresponds with the use of the new velu's formulaes

                        E_prev = list(E_i)
                        if self.tuned:
                            self.formula.set_parameters_velu(
                                self.formula.sJ_list[pos],
                                self.formula.sI_list[pos],
                                pos,
                            )

                        else:
                            # -------------------------------------------------------------
                            # Parameters sJ and sI correspond with the parameters b and b' from example 4.12 of https://eprint.iacr.org/2020/341
                            # These paramters are required in kps, xisog, and xeval
                            if self.formula.L[pos] == 3:
                                b = 0
                                c = 0
                            else:
                                b = int(
                                    floor(
                                        sqrt(self.formula.L[pos] - 1)
                                        / 2.0
                                    )
                                )
                                c = int(
                                    floor(
                                        (self.formula.L[pos] - 1.0)
                                        / (4.0 * b)
                                    )
                                )

                            self.formula.set_parameters_velu(b, c, pos)

                    self.formula.kps(ramifications[-1][0], E_i, pos)

                    # New isogeny construction
                    E_i = self.formula.xisog(E_i, pos)

                    # Horizontal edges are performed
                    for j in range(0, len(moves) - 1, 1):

                        # Swap points in E[\pi - 1] and E[\pi + 1]
                        (
                            ramifications[j][0][0],
                            ramifications[j][1][0],
                        ) = cswap(
                            ramifications[j][0][0], ramifications[j][1][0], c_i
                        )
                        (
                            ramifications[j][0][1],
                            ramifications[j][1][1],
                        ) = cswap(
                            ramifications[j][0][1], ramifications[j][1][1], c_i
                        )

                        if self.formula_name == 'tvelu' or (
                            self.formula_name == 'hvelu'
                            and self.formula.L[pos]
                            <= self.formula.HYBRID_BOUND
                        ):
                            # This branchs corresponds with the use of the tradicional velu's formulaes
                            # T_- (or T_)
                            ramifications[j][0] = self.formula.xeval(
                                ramifications[j][0], pos
                            )
                            # T_+ or (T_+)
                            ramifications[j][1] = self.formula.xeval(
                                ramifications[j][1], pos
                            )
                        else:
                            # This branchs corresponds with the use of the new velu's formulaes
                            # T_- (or T_)
                            ramifications[j][0] = self.formula.xeval(
                                ramifications[j][0], E_prev
                            )
                            # T_+ or (T_+)
                            ramifications[j][1] = self.formula.xeval(
                                ramifications[j][1], E_prev
                            )

                        # Multiplying by the current small odd prime number in order to decrease the order of both points T_+ and T_-
                        ramifications[j][1] = self.curve.xmul(
                            ramifications[j][1], E_i, pos
                        )

                        # Undo swap of points in E[\pi - 1] and E[\pi + 1]
                        (
                            ramifications[j][0][0],
                            ramifications[j][1][0],
                        ) = cswap(
                            ramifications[j][0][0], ramifications[j][1][0], c_i
                        )
                        (
                            ramifications[j][0][1],
                            ramifications[j][1][1],
                        ) = cswap(
                            ramifications[j][0][1], ramifications[j][1][1], c_i
                        )

                    b_i = (
                        isequal[u[pos] == 1] ^ isequal[u[pos] == -1]
                    )  # 0 if u[pos] != +1,-1; otherwise, 1 [This ask must be performed in constant-time]
                    v[pos] -= 1
                    u[pos] -= s_i * (
                        1 + b_i
                    )  # reduced by 1 unit if it was different from +1 and -1; otherwise, reduced by 2 units

                else:

                    for j in range(0, len(moves) - 1, 1):

                        (
                            ramifications[j][0][0],
                            ramifications[j][1][0],
                        ) = cswap(
                            ramifications[j][0][0], ramifications[j][1][0], c_i
                        )
                        (
                            ramifications[j][0][1],
                            ramifications[j][1][1],
                        ) = cswap(
                            ramifications[j][0][1], ramifications[j][1][1], c_i
                        )
                        ramifications[j][1] = self.curve.xmul(
                            ramifications[j][1], E_i, pos
                        )
                        (
                            ramifications[j][0][0],
                            ramifications[j][1][0],
                        ) = cswap(
                            ramifications[j][0][0], ramifications[j][1][0], c_i
                        )
                        (
                            ramifications[j][0][1],
                            ramifications[j][1][1],
                        ) = cswap(
                            ramifications[j][0][1], ramifications[j][1][1], c_i
                        )
            else:
                # This branch only depends on randomness

                # At this step, ramifications[-1] is the i-th leaf
                (
                    ramifications[-1][0][0],
                    ramifications[-1][1][0],
                ) = cswap(
                    ramifications[-1][0][0], ramifications[-1][1][0], c_i
                )
                (
                    ramifications[-1][0][1],
                    ramifications[-1][1][1],
                ) = cswap(
                    ramifications[-1][0][1], ramifications[-1][1][1], c_i
                )

                if self.curve.isinfinity(ramifications[-1][0]) == False:

                    for j in range(0, len(moves) - 1, 1):

                        ramifications[j][0] = self.curve.xmul(
                            ramifications[j][0], E_i, pos
                        )
                        ramifications[j][1] = self.curve.xmul(
                            ramifications[j][1], E_i, pos
                        )
                else:

                    for j in range(0, len(moves) - 1, 1):

                        (
                            ramifications[j][0][0],
                            ramifications[j][1][0],
                        ) = cswap(
                            ramifications[j][0][0], ramifications[j][1][0], c_i
                        )
                        (
                            ramifications[j][0][1],
                            ramifications[j][1][1],
                        ) = cswap(
                            ramifications[j][0][1], ramifications[j][1][1], c_i
                        )
                        ramifications[j][1] = self.curve.xmul(
                            ramifications[j][1], E_i, pos
                        )
                        (
                            ramifications[j][0][0],
                            ramifications[j][1][0],
                        ) = cswap(
                            ramifications[j][0][0], ramifications[j][1][0], c_i
                        )
                        (
                            ramifications[j][0][1],
                            ramifications[j][1][1],
                        ) = cswap(
                            ramifications[j][0][1], ramifications[j][1][1], c_i
                        )

            moves.pop()
            ramifications.pop()

        pos = self.formula.L.index(
            L[0]
        )  # Current element of self.formula.L to be required
        s_i = sign(e[pos])  # Sign of e[pos]
        c_i = (s_i + 1) // 2  # Constant-swap of T_+ and T_-

        # T_+ or T_- ?
        # Root
        ramifications[0][0][0], ramifications[0][1][0] = cswap(
            ramifications[0][0][0], ramifications[0][1][0], c_i
        )
        ramifications[0][0][1], ramifications[0][1][1] = cswap(
            ramifications[0][0][1], ramifications[0][1][1], c_i
        )

        if self.curve.isinfinity(ramifications[0][0]) == False:

            if m[pos] > 0:
                if self.formula_name != 'tvelu':
                    # This branchs corresponds with the use of the new velu's formulaes

                    if self.tuned:
                        self.formula.set_parameters_velu(
                            self.formula.sJ_list[pos],
                            self.formula.sI_list[pos],
                            pos,
                        )

                    else:
                        # -------------------------------------------------------------
                        # Parameters sJ and sI correspond with the parameters b and b' from example 4.12 of https://eprint.iacr.org/2020/341
                        # These paramters are required in kps, xisog, and xeval
                        if self.formula.L[pos] == 3:
                            b = 0
                            c = 0
                        else:
                            b = int(
                                floor(
                                    sqrt(self.formula.L[pos] - 1) / 2.0
                                )
                            )
                            c = int(
                                floor(
                                    (self.formula.L[pos] - 1.0)
                                    / (4.0 * b)
                                )
                            )

                        self.formula.set_parameters_velu(b, c, pos)

                self.formula.kps(ramifications[0][0], E_i, pos)
                E_i = self.formula.xisog(E_i, pos)

                b_i = (
                    isequal[u[pos] == 1] ^ isequal[u[pos] == -1]
                )  # 0 if u[pos] != +1,-1; otherwise, 1 [This ask must be performed in constant-time]
                v[pos] -= 1
                u[pos] -= s_i * (
                    1 + b_i
                )  # reduced by 1 unit if it was different from +1 and -1; otherwise, reduced by 2 units

        return E_i, v, u