示例#1
0
def main(ctx, **kwargs):
    """

    \b
      ,-~~-.___.          
     / |  '     \\        
    (  )         0        
     \_/-, ,----'         
        ====           // 
       /  \-'~;    /~~~(O)
      /  __/~|   /       |
    =(  _____| (_________|

    """
    algo_args = kwargs.copy()
    algorithm = algo_args.pop('algorithm')
    algo_args.pop('benchmark')
    if algorithm == 'csidh':
        from sibc.csidh import CSIDH

        algo = CSIDH(**algo_args)
    elif algorithm == 'bsidh':
        from sibc.bsidh import BSIDH
        algo_args.pop('style')
        algo_args.pop('exponent')
        algo = BSIDH(**algo_args)
    else:
        click.echo('algorithm not implemented')
        raise Exit(1)
    kwargs['algo'] = algo
    ctx.meta['sibc.kwargs'] = attrdict(kwargs)
示例#2
0
def csidh_precompute_strategy(ctx):
    """ Precomputation of optimal strategies """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    coeff = algo.curve.coeff
    p = algo.params.p
    L = algo.params.L
    m = algo.params.m
    n = algo.params.n
    SQR, ADD = algo.curve.SQR, algo.curve.ADD
    init_runtime = algo.field.init_runtime
    validate = algo.curve.issupersingular
    measure = algo.curve.measure
    GAE_at_0 = algo.gae.GAE_at_0
    GAE_at_A = algo.gae.GAE_at_A
    strategy_block_cost = algo.gae.strategy_block_cost
    random_exponents = algo.gae.random_exponents
    print_exponents = algo.gae.print_exponents

    assert setting.uninitialized, 'option -u (--uninitialized) is required!'

    if algo.formula.name != 'tvelu':
        set_parameters_velu = algo.formula.set_parameters_velu

    temporal_m = list(set(m))
    if len(temporal_m) > 1:
        # Maximum number of degree-(l_i) isogeny constructions is m_i (different for each l_i)
        bounds = '-diffbounds'
    else:
        # Maximum number of degree-(l_i) isogeny constructions is m (the same for each l_i)
        bounds = '-samebounds'

    tuned = {True: '-tuned', False: ''}[setting.tuned]
    multievaluation = {
        True: 'scaled',
        False: 'unscaled'
    }[setting.multievaluation]
    # List of Small Odd Primes, L := [l_0, ..., l_{n-1}]
    m_prime = [geometric_serie(m[k], L[k]) for k in range(n)]
    r_out, L_out, R_out = rounds(m_prime[::-1], n)
    for j in range(0, len(r_out), 1):

        R_out[j] = list([L[::-1][k] for k in R_out[j]])
        L_out[j] = list([L[::-1][k] for k in L_out[j]])

    file_path = ("data/strategies/" + algo.curve.model + '/csidh/' + 'csidh' +
                 '-' + setting.prime + '-' + setting.style + '-e' +
                 setting.exponent + bounds + '-' + setting.formula + '-' +
                 multievaluation + tuned)
    file_path = resource_filename('sibc', file_path)
    print("// Strategies to be computed")
    C_out, L_out, R_out, S_out, r_out = strategy_block_cost(L[::-1], m[::-1])
    f = open(file_path, 'w')
    for i in range(0, len(r_out)):

        f.writelines(' '.join([str(tmp) for tmp in S_out[i]]) + '\n')

    f.close()

    return attrdict(name='csidh-precompute-strategy', **locals())
示例#3
0
文件: ijk.py 项目: adelapie/sibc
def csidh_ijk(ctx):
    """ Velusqrt parameters as C-code headers files """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    L = algo.params.L
    n = algo.params.n
    m = algo.params.m
    sI_list = algo.formula.sI_list
    sJ_list = algo.formula.sJ_list

    # ---
    k = 3  # Number of rows (format of the list)
    # ---

    print("#ifndef _IJK_%s_H_" % setting.prime)
    print("#define _IJK_%s_H_" % setting.prime)
    print("")
    assert n == len(L)

    print("#ifdef _MONT_C_CODE_")
    print("// The list of the bitlength of each SOP")
    printl("static uint64_t bL[]", [bitlength(l) for l in L], n // k + 1)
    print("#endif")

    print("")
    print("#ifdef _ISOG_H_")
    print(
        "\n// The list of Small Odd Primes (SOPs) is stored such that l_0 < l_1 < ... < l_{n-1}"
    )
    printl("static uint64_t L[]", L, n // k + 1)

    assert n == len(sI_list)
    assert n == len(sJ_list)

    sK_list = []
    for i in range(0, n, 1):
        assert sJ_list[i] <= sI_list[i]
        sK_list.append(((L[i] - 2 - 4 * sJ_list[i] * sI_list[i] - 1) // 2) + 1)
        assert sK_list[i] >= 0

    print("")
    print("#ifndef _C_CODE_")
    print(
        "// Sizes for the sets I, J, and K required in the new velusqrt formulae"
    )
    printl("static int sizeI[]", sI_list, n // k + 1)
    printl("static int sizeJ[]", sJ_list, n // k + 1)
    printl("static int sizeK[]", sK_list, n // k + 1)
    print("#endif")

    print("")
    print("#define sI_max %d" % (max(sI_list)))
    print("#define sJ_max %d" % (max(sJ_list)))
    print("#define sK_max %d" % (L[n - 1] // 2 + 1))
    print("#endif")

    print(
        "\n#endif /* required framework for the #I, #J, and #K, which is used in new velusqrt fomurlae on CSIDH-%s */"
        % setting.prime[1:])
    return attrdict(name='ijk', **locals())
示例#4
0
def bsidh_precompute_strategy(ctx):
    """ Precomputation of optimal strategies """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    SIDp = algo.strategy.SIDp
    SIDm = algo.strategy.SIDm

    assert setting.uninitialized, 'option -u (--uninitialized) is required!'

    tuned = {True: '-tuned', False: ''}[setting.tuned]
    multievaluation = {
        True: 'scaled',
        False: 'unscaled'
    }[setting.multievaluation]
    file_path = ("data/strategies/" + algo.curve.model + '/bsidh/' + 'bsidh' +
                 '-' + setting.prime + '-' + setting.formula + '-' +
                 multievaluation + tuned)
    file_path = resource_filename('sibc', file_path)
    print("// Strategies to be computed")
    # List of Small Isogeny Degree, Lp := [l_0, ..., l_{n-1}] [We need to
    # include case l=2 and l=4]
    Sp, Cp = algo.strategy.dynamic_programming_algorithm(SIDp[::-1], len(SIDp))
    # List of Small Isogeny Degree, Lm := [l_0, ..., l_{n-1}]
    Sm, Cm = algo.strategy.dynamic_programming_algorithm(SIDm[::-1], len(SIDm))
    f = open(file_path, 'w')
    f.writelines(' '.join([str(tmp) for tmp in Sp]) + '\n')
    f.writelines(' '.join([str(tmp) for tmp in Sm]) + '\n')
    f.close()

    return attrdict(name='bsidh-precompute-strategy', **locals())
示例#5
0
    def __init__(
        self,
        curvemodel,
        prime,
        formula,
        style,
        exponent,
        tuned,
        multievaluation,
        uninitialized,
        verbose,
    ):
        self.curvemodel = curvemodel
        self.prime = prime
        self.style = style
        self._exponent = exponent
        self.tuned = tuned
        self.uninitialized = uninitialized
        self.multievaluation = multievaluation
        self.params = attrdict(parameters['csidh'][prime])
        self.params.update(self.params[style])
        self.p_bytes = (self.params.p_bits + 8 - (self.params.p_bits % 8)) // 8

        if self.curvemodel == 'montgomery':
            self.isogeny = MontgomeryIsogeny(formula,
                                             uninitialized=self.uninitialized)
            self.curve = MontgomeryCurve(prime)
            self.field = self.curve.field
        else:
            self.curve = None
            raise NotImplemented

        self.formula = self.isogeny(self.curve, self.tuned,
                                    self.multievaluation)

        if self.style == 'df':
            self.gae = Gae_df(prime, self.tuned, self.curve, self.formula)
        elif self.style == 'wd1':
            self.gae = Gae_wd1(prime, self.tuned, self.curve, self.formula)
        elif self.style == 'wd2':
            self.gae = Gae_wd2(prime, self.tuned, self.curve, self.formula)
        else:
            self.gae = NotImplemented
示例#6
0
文件: __init__.py 项目: adelapie/sibc
    def __init__(
        self,
        curvemodel,
        prime,
        formula,
        tuned,
        multievaluation,
        uninitialized,
        verbose
    ):

        self.params = attrdict(parameters['bsidh'][prime])
        self.p_bytes = (self.params.p_bits + (self.params.p_bits % 8)) // 8
        self.prime = prime
        self.tuned = tuned
        self.uninitialized = uninitialized
        self.multievaluation = multievaluation
        self.verbose = verbose

        random = SystemRandom()

        if curvemodel == 'montgomery':
            self.isogeny = MontgomeryIsogeny(formula, uninitialized = self.uninitialized)
            self.curve = MontgomeryCurve(prime)
            self.field = self.curve.field
            self.basefield = self.curve.field.basefield
        else:
            self.curve = None
            raise NotImplemented

        self.formula = self.isogeny(self.curve, self.tuned, self.multievaluation)

        if self.formula is not None and self.curve is not None:
            self.strategy = Strategy(prime, self.tuned, self.curve, self.formula)
        else:
            self.strategy = None
            raise NotImplemented
示例#7
0
def csidh_precompute_parameters(ctx):
    """ Precomputation of tuned velusqrt parameters """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    L = algo.params.L
    n = algo.params.n
    m = algo.params.m
    generators = algo.curve.generators
    set_parameters_velu = algo.formula.set_parameters_velu
    init_runtime = algo.field.init_runtime
    kps = algo.formula.kps
    xisog = algo.formula.xisog
    xeval = algo.formula.xeval
    sJ_list = algo.formula.sJ_list
    xmul = algo.curve.xmul
    HYBRID_BOUND = algo.formula.HYBRID_BOUND

    A = [algo.curve.field(2), algo.curve.field(4)]
    if True:

        # T_p belongs to E[pi - 1]
        # T_m belongs to E[pi + 1]
        T_p, T_m = generators(A)

    else:

        # T_m belongs to E[pi - 1]
        # T_p belongs to E[pi + 1]
        T_m, T_p = generators(A)

    assert len(L) == n

    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, n, 1):

            # -------------------------------------------------------------
            # Random kernel point
            Tp = list(T_p)
            for i in range(0, n, 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)))

            b += 1
            parameters[str(idx)] = []
            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()
                kps(Tp, A, idx)

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

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

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

                # -------------------------------------------------------------
                # xeval bench
                init_runtime()
                if L[idx] <= HYBRID_BOUND:
                    Tm = xeval(T_m, idx)
                else:
                    Tm = xeval(T_m, A)

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

                # assert(validate(B))
                parameters[str(idx)].append(
                    (b, c,
                     [algo.field.fpmul, algo.field.fpsqr,
                      algo.field.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])
    return attrdict(name='csidh-precompute-parameters', **locals())
示例#8
0
文件: header.py 项目: JJChiDguez/sibc
def csidh_header(ctx):
    """ Optimal strategies as C-code headers files """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    L = algo.params.L
    n = algo.params.n
    m = algo.params.m

    strategy_block_cost = algo.gae.strategy_block_cost
    basis = numpy.eye(n, dtype=int)
    measure = algo.curve.measure

    # ==========================================================================

    if algo.formula.name != 'tvelu':
        set_parameters_velu = algo.formula.set_parameters_velu

    temporal_m = list(set(m))
    if len(temporal_m) > 1:
        # Maximum number of degree-(l_i) isogeny constructions is m_i (different for each l_i)
        bounds = '-diffbounds'
    else:
        # Maximum number of degree-(l_i) isogeny constructions is m (the same for each l_i)
        bounds = '-samebounds'

    # List of Small Odd Primes, L := [l_0, ..., l_{n-1}]
    m_prime = [geometric_serie(m[k], L[k]) for k in range(n)]
    r_out, L_out, R_out = rounds(m_prime[::-1], n)
    for j in range(0, len(r_out), 1):

        R_out[j] = list([L[::-1][k] for k in R_out[j]])
        L_out[j] = list([L[::-1][k] for k in L_out[j]])

    file_path = ("data/strategies/" + algo.curve.model + '/' + 'csidh/csidh' +
                 '-' + setting.prime + '-' + setting.style + '-e' +
                 setting.exponent + bounds + '-' + setting.formula + '-' +
                 algo.formula.multievaluation_name + algo.formula.tuned_name)
    file_path = resource_filename('sibc', file_path)
    f = open(file_path)
    S_out = []
    for i in range(0, len(r_out), 1):

        tmp = f.readline()
        tmp = [int(b) for b in tmp.split()]
        S_out.append(tmp)

    f.close()

    if (len(set(m)) == 1) or ((len(set(m)) == 2) and (0 in set(m))):
        L_out = list([L_out[0]])
        R_out = list([R_out[0]])
        S_out = list([S_out[0]])
        r_out = list([r_out[0]])
    # ---
    k = 3  # Number of rows (format of the list)
    # ---

    print("#ifndef _STRATEGIES_H_")
    print("#define _STRATEGIES_H_\n")
    print("#include <inttypes.h>\n\n")
    print(
        "// This script assumes the C-code implementation has the list of Small Odd Primes (SOPs) stored such that l_0 < l_1 < ... < l_{n-1}"
    )
    print("// Recall, the strategies process from small SOPs to large SOPs.\n")

    for i in range(len(r_out)):
        print(
            "// -----------------------------------------------------------------------------------------------------------------------------------"
        )
        print("// Strategy number %d\n" % (i))
        L_string = "static uint32_t L%d[] " % (i)
        R_string = "static uint32_t W%d[] " % (i)
        S_string = "static uint32_t S%d[] " % (i)

        printl(
            L_string,
            [L.index(l_i) for l_i in L_out[i]],
            len(L_out[i]) // k + 1,
        )
        if R_out[i] != []:
            printl(
                R_string,
                [L.index(r_i) for r_i in R_out[i]],
                len(R_out[i]) // k + 1,
            )
        else:
            print("static uint32_t W%d[1];" % i)
        if S_out[i] != []:
            printl(S_string, S_out[i], len(S_out[i]) // k + 1)
        else:
            print("static uint32_t S%d[1];" % i)

    print("\n")
    print(
        "// -----------------------------------------------------------------------------------------------------------------------------------"
    )
    print(
        "// -----------------------------------------------------------------------------------------------------------------------------------"
    )
    print("#define NUMBER_OF_DIFFERENT_STRATEGIES  %d" % len(L_out))
    print("")
    L_string = (
        "static uint32_t *L_STRATEGY[NUMBER_OF_DIFFERENT_STRATEGIES] = {\n\t")
    R_string = (
        "static uint32_t *W_STRATEGY[NUMBER_OF_DIFFERENT_STRATEGIES] = {\n\t")
    S_string = "static uint32_t *S[NUMBER_OF_DIFFERENT_STRATEGIES] = {\n\t"

    tmp_sizes = "static uint32_t NUMBER_OF_PRIMES[] = {\n\t"
    tmp_round = "static uint8_t ROUNDS[] = {\n\t"

    for i in range(len(L_out) - 1):

        L_string = L_string + "L%d, " % (i)
        R_string = R_string + "W%d, " % (i)
        S_string = S_string + "S%d, " % (i)

        tmp_sizes = tmp_sizes + "%3d," % (len(L_out[i]))
        tmp_round = tmp_round + "%3d," % (r_out[i])

    L_string = L_string + "L%d\n\t};" % (len(L_out) - 1)
    R_string = R_string + "W%d\n\t};" % (len(L_out) - 1)
    S_string = S_string + "S%d\n\t};" % (len(L_out) - 1)

    tmp_sizes = tmp_sizes + "%3d\n\t};" % (len(L_out[len(L_out) - 1]))
    tmp_round = tmp_round + "%3d\n\t};" % (r_out[len(L_out) - 1])

    print(
        "// L_STRATEGY[i] determines the small odd primes l_i per each strategy"
    )
    print(L_string)
    print("\n// W_STRATEGY[i] determines L - L_STRATEGY[i]")
    print(R_string)
    print(
        "\n// S_STRATEGY[i] determines the optimal strategy for L_STRATEGY[i]")
    print(S_string)

    print("\n// Number of primes for each strategy")
    print(tmp_sizes)
    print("")
    print("// Number of rounds per each different strategy")
    print(tmp_round)

    print("")
    print("// Maximum number of degree-(l_i) isogeny constructions")
    printl("static uint8_t M[]", m, n // k + 1)

    STYLE_NAME = {
        'wd2': 'OAYT-style',
        'wd1': 'MCR-style',
        'df': 'dummy-free-style',
    }[setting.style]
    print(
        "\n#endif /* required framework for the strategies to be used in CSIDH-%s using %s */"
        % (setting.prime[1:], STYLE_NAME))
    # Need empty line at EOF for -Wnewline-eof
    print("")
    return attrdict(name='header', **locals())
示例#9
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())
示例#10
0
def plot_strategy(ctx):
    """
    draw strategy graphs as a subgraph Discrete Right Triangle (DRT)
    """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    if setting.algorithm == 'csidh':
        L = algo.params.L
        m = algo.params.m
        n = algo.params.n

        temporal_m = list(set(m))
        if len(temporal_m) > 1:
            # Maximum number of degree-(l_i) isogeny constructions is m_i (different for each l_i)
            bounds = '-diffbounds'
        else:
            # Maximum number of degree-(l_i) isogeny constructions is m (the same for each l_i)
            bounds = '-samebounds'

        # List of Small Odd Primes, L := [l_0, ..., l_{n-1}]
        m_prime = [geometric_serie(m[k], L[k]) for k in range(n)]
        r_out, L_out, R_out = rounds(m_prime[::-1], n)
        for j in range(0, len(r_out), 1):

            R_out[j] = list([L[::-1][k] for k in R_out[j]])
            L_out[j] = list([L[::-1][k] for k in L_out[j]])

        file_path = ("data/strategies/" + algo.curve.model + '/' + 'csidh' +
                     '-' + setting.prime + '-' + setting.style + '-e' +
                     setting.exponent + bounds + '-' + setting.formula + '-' +
                     algo.formula.multievaluation_name +
                     algo.formula.tuned_name)
        file_path = resource_filename('sibc', file_path)
        f = open(file_path)
        S_out = []
        for i in range(0, len(r_out), 1):

            tmp = f.readline()
            tmp = [int(b) for b in tmp.split()]
            S_out.append(tmp)

        f.close()

        file_path = ('csidh' + '-' + setting.prime + '-' + setting.style +
                     '-e' + setting.exponent + bounds + '-' + setting.formula +
                     '-' + algo.formula.multievaluation_name +
                     algo.formula.tuned_name + '-' + algo.curve.model)

    elif setting.algorithm == 'bsidh':
        file_path = ("data/strategies/" + algo.curve.model + '/' + 'bsidh' +
                     '-' + setting.prime + '-' + setting.formula + '-' +
                     algo.formula.multievaluation_name +
                     algo.formula.tuned_name)
        file_path = resource_filename('sibc', file_path)
        f = open(file_path)
        S_out = []
        # Corresponding to the list of Small Isogeny Degree, Lp := [l_0, ...,
        # l_{n-1}] [We need to include case l=2 and l=4]
        tmp = f.readline()
        tmp = [int(b) for b in tmp.split()]
        S_out.append(list(tmp))
        # Corresponding to the list of Small Isogeny Degree, Lm := [l_0, ...,
        # l_{n-1}]
        tmp = f.readline()
        tmp = [int(b) for b in tmp.split()]
        S_out.append(list(tmp))
        f.close()

        file_path = ('bsidh' + '-' + setting.prime + '-' + setting.style +
                     '-' + setting.formula + '-' +
                     algo.formula.multievaluation_name +
                     algo.formula.tuned_name + '-' + algo.curve.model)

    else:
        print("only csidh and bsidh are implemented")
        click.Exit(1)

    # ----
    for idx in range(0, len(S_out), 1):
        S = S_out[idx]
        n = len(S) + 1

        # Strategy written as a graph
        vertexes, vertex_colors, edges, edge_colors = strategy_evaluation(S, n)

        # Simba method written as a graph
        # vertexes, vertex_colors, edges, edge_colors = simba(n, 3)

        # All the Discrete Right Triangle
        # vertexes, vertex_colors, edges, edge_colors = DRT(n)
        G = nx.Graph()

        # Adding nodes in specific positions
        G.add_nodes_from(list(range(len(vertexes))))

        nx.set_node_attributes(G, vertexes, 'pos')
        # Adding edges with specific colors
        for i in range(len(edges)):
            G.add_edge(edges[i][0], edges[i][1], color=edge_colors[i])

        # Setting variables for a pretty plot of the graph
        edges = G.edges()
        edge_colors = [G[u][v]['color'] for u, v in edges]
        weights = [6 for u, v in edges]
        vertex_sizes = [24] * len(vertexes)

        # Finally, the graph will be plotted
        plt.figure(1, figsize=(17, 17))
        nx.draw(
            G,
            vertexes,
            node_color=['black'] * len(vertexes),
            node_size=vertex_sizes,
            edge_color=edge_colors,
            width=weights,
            edge_labels=True,
        )

        # Saving the graph as a .PNG figure
        file_name = file_path + '-id' + str(idx) + '.png'
        plt.savefig(file_name)
        print("saving graph: " + file_name)
        # plt.show()
        plt.close()

    print(
        "// The strategies have been plotted and stored in the current directory"
    )

    def DRT(n):

        vertexes = dict()  # list of the position of each node
        vertex_colors = (
            []
        )  # color of each node: red for the leaves, otherwise color is set to white
        acc = 0

        # Different shape of the isogeny graph
        for i in range(n):
            for j in range(n - 1 - i):
                vertex_colors.append('black')
                vertexes[acc] = (i, -j)
                acc += 1

        return vertexes, vertex_colors, [], []

    return attrdict(name='plot-strategy', **locals())
示例#11
0
文件: polymul.py 项目: adelapie/sibc
def PolyMul(field, maxdeg=None, mindeg=64):

    # ----------------------------------------------------------------------------------
    # Table of 2 raised to a negative power
    if maxdeg != None:
        inverse_of_two = (field(2)**-1)
        max_exp = 2 * int(floor(sqrt(maxdeg)))
        negative_powers_of_two = dict()
        negative_powers_of_two[1] = 1

        j = 1
        j_next = 1
        for i in range(0, max_exp, 1):
            j_next *= 2
            negative_powers_of_two[j_next] = (negative_powers_of_two[j] *
                                              inverse_of_two)
            j = j_next

    # ----------------------------------------------------------------------------------
    # Here, a polynomial is represented as a list of integers.
    def print_poly(h, lenh):

        # Just a pretty way for printing polynomials
        print(
            reduce(lambda x, y: x + ' + ' + y,
                   [f'({h[i]}) * (x ^ {i})' for i in range(0, lenh, 1)]))
        return None

    def karatsuba_mul(f, flen, g, glen):
        """
        Karatsuba style multiplication of two polynomials
        """

        if flen < glen:
            return poly_mul(g, glen, f, flen)

        # At this step, we ensure flen >= glen
        if [] == g or [] == f:

            # Multiplication by 0, here the zero polynomial is represented as the empty list
            return []

        if glen == 1:

            # Multipication by a constant
            return [(f[i] * g[0]) for i in range(0, flen, 1)]

        # At this step, we ensure flen >= glen >= 2
        if flen == 2:

            # Multiplication of linear polynomials over fp
            # Thus c(x) = (a * b)(x) is a quadratic polynomial
            c = list([0, 0, 0])

            c[0] = (f[0] * g[0])  # coeff of x^0
            c[2] = (f[1] * g[1])  # coeff of x^2
            f01 = (f[0] + f[1])
            g01 = (g[0] + g[1])
            c[1] = (f01 * g01)
            c[1] = (c[1] - c[0])
            c[1] = (c[1] - c[2])  # coeff of x^1
            return c

        if flen == 3:

            # f(x) is a quadratic polynomial
            if glen == 2:

                # g(x) is a linear polynomial
                c = list([0, 0, 0,
                          0])  # Thus c(x) = (f * g)(x) is a cubic polynomial

                c[0] = (f[0] * g[0])  # coeff of x^0
                c[2] = (f[1] * g[1])
                f01 = (f[0] + f[1])
                g01 = (g[0] + g[1])
                c[1] = (f01 * g01)
                c[1] = (c[1] - c[0])
                c[1] = (c[1] - c[2])  # coeff of x^1
                c[3] = (f[2] * g[1])  # coeff of x^3
                f2g0 = (f[2] * g[0])
                c[2] = (c[2] + f2g0)  # coeff of x^2
                return c

            if glen == 3:

                # g(x) is a a quadratic polynomial

                karatsuba_0 = karatsuba_mul(f[:1], 1, g[:1],
                                            1)  # field multiplication
                karatsuba_1 = karatsuba_mul(
                    f[1:flen], 2, g[1:glen],
                    2)  # multiplication of two linear polynomials

                # Middle part
                f_01 = (f[0] + f[1])
                f_02 = (f[0] + f[2])
                g_01 = (g[0] + g[1])
                g_02 = (g[0] + g[2])

                t_01 = (f_01 * g_01)
                t_02 = (f_02 * g_02)

                l_coeff = (t_01 - karatsuba_0[0])
                l_coeff = (l_coeff - karatsuba_1[0])
                q_coeff = (t_02 - karatsuba_0[0])
                q_coeff = (q_coeff - karatsuba_1[2])
                q_coeff = (q_coeff + karatsuba_1[0])

                return list(karatsuba_0 + [l_coeff, q_coeff] + karatsuba_1[1:])

        else:

            # Multiplication of polynomials of degree >= 3 over fp
            nf = flen // 2
            mf = flen - nf
            f_low = f[:nf]
            f_high = f[nf:flen]

            if glen <= nf:

                c0 = karatsuba_mul(f_low, nf, g[:glen], glen)
                c1 = karatsuba_mul(f_high, mf, g[:glen], glen)
                return (c0[:nf] + [(c0[nf + i] + c1[i])
                                   for i in range(0, glen - 1, 1)] +
                        c1[(glen - 1):])

            mg = glen - nf
            g_low = g[:nf]
            g_high = g[nf:glen]

            f_mid = [(f_low[i] + f_high[i])
                     for i in range(0, nf, 1)] + f_high[nf:]

            fg_low = karatsuba_mul(f_low, nf, g_low, nf)
            fg_high = karatsuba_mul(f_high, mf, g_high, mg)

            if mg < mf:

                g_mid = [(g_low[i] + g_high[i])
                         for i in range(0, mg, 1)] + g_low[mg:]
                fg_mid = poly_mul(f_mid, mf, g_mid, nf)

            else:

                g_mid = [(g_low[i] + g_high[i])
                         for i in range(0, nf, 1)] + g_high[nf:]
                fg_mid = poly_mul(f_mid, mf, g_mid, mg)

            fg_mid = [
                (fg_mid[i] - fg_high[i]) for i in range(0, mf + mg - 1, 1)
            ] + fg_mid[(mf + mg - 1):]
            fg_mid = [(fg_mid[i] - fg_low[i])
                      for i in range(0, 2 * nf - 1, 1)] + fg_mid[(2 * nf - 1):]

            return (fg_low[:nf] + [(fg_low[nf + i] + fg_mid[i])
                                   for i in range(0, nf - 1, 1)] +
                    [fg_mid[nf - 1]] + [(fg_mid[nf + i] + fg_high[i])
                                        for i in range(0, mf - 1)] +
                    fg_high[(mf - 1):])

    def qring_mul(f, g, e):

        n = len(f)
        m = len(f[0])

        if (n == 1) and (m <= 8):

            # The product f[1] * g[1] is directly computed in B = fp[x] / (x^m + 1)
            h = karatsuba_mul(f[0], m, g[0], m)  # Polynomial multiplication

            # Next, we reduce h modulo (x^m + 1)
            h_0 = h[:m]
            h_1 = h[m:]
            h_1 = h_1 + [0] * (len(h_0) - len(h_1))

            return [[(h_0[i] - h_1[i]) for i in range(0, m, 1)]]

        elif (n == 1) and (m > 8):

            # We need to  embed f[1] and g[1] in B[y]/(y^n2+1) being
            # B = Fp[x]/(x^m2+1) where n2, andm2 are computed as follows
            s = int(floor(log(m, 2))) // 2
            m2 = 2**(s + 1)
            n2 = (2 * m) // m2
            hm2 = m2 // 2  # notice that n2*hm2 = m

            # Now, F and G will corresponds with the  images of f[1] and g[1],
            # respectively, in B2[y]/(y^n2+1); here, B2 = Fp[x]/(x^hm2+1).
            # Notice B2[y]/(y^n2+1) is 'contained' in B[y]/(y^n2+1)
            F = []
            G = []
            ind = -1

            for j in range(0, n2, 1):

                Fj = []
                Gj = []

                for i in range(0, hm2, 1):
                    ind += 1
                    Fj.append(f[0][ind])
                    Gj.append(g[0][ind])

                for i in range(0, hm2, 1):
                    Fj.append(0)
                    Gj.append(0)

                F.append(Fj)
                G.append(Gj)

            # Next, we recursively multiply F and G but now in B[y]/(y^n2+1) = B[y]/(y^n2-x^m2)
            H = qring_mul(F, G, m2)
            # At this point, H is equal to  n2*F*G, so we need to divide by n2
            # in2 =  fp.fp_inv(n2)   # This inverse can be omited (we only care about polynomials with the same roots)

            # H lives in B[y]/(y^n2+1), 'contained' in Fp[x,y]/(y^n2+1), so to recover
            # the corresponding element h in Fp[x][x^m+1], in H we substitute y by x^hm2.
            h = list([0] * m)
            degy = 0
            func = {0: (lambda x, y: x + y), 1: lambda x, y: x - y}
            for j in range(0, n2, 1):

                deg = degy
                for i in range(0, m2, 1):

                    # for a polynomial position (l,k) (that is position [j-1,i-1] when viewed
                    # as sequences), deg is the degree of x^k*y^l after substitution, namely,
                    # deg = l*hm2 + k. Sometimes we have deg>m-1, this is why case we have to
                    # take residues (deg mod m) and also flip the sign of H(l,k) when deg>m-1
                    q, r = divmod(deg, m)
                    # (-1)^q determines if  or  will be needed
                    hr = H[j][i]
                    h[r] = func[q % 2](h[r], hr)
                    deg += 1

                degy += hm2

            return [[(h[i] * negative_powers_of_two[n2])
                     for i in range(0, m, 1)]]

        else:

            # The strategy proceeds by spliting the degree-n problem over B=Fp[x]/(x^m+1) into
            # two degree-n/2 over B, and then each of the two problems is handled recursively
            # until reaching degree-0 products over B

            # However, f*g is to be computed in B[y]/(y^n-x^e) = B[y]/[(y^n2)^2-(x^e2)^2]
            n2 = n // 2
            e2 = e // 2

            F1, F2 = [], []
            G1, G2 = [], []
            # (-1)^q determines if an addition or substraction will be needed
            func = {0: (lambda x, y: x + y), 1: lambda x, y: x - y}
            for j in range(0, n2, 1):

                Fj1, Fj2 = [], []
                Gj1, Gj2 = [], []
                for i in range(0, m, 1):

                    q, r = divmod(i - e2, m)
                    sgn = q % 2
                    # ---
                    Fj1.append(func[sgn](f[j][i], f[n2 + j][r]))
                    Gj1.append(func[sgn](g[j][i], g[n2 + j][r]))
                    # ---
                    Fj2.append(func[1 - sgn](f[j][i], f[n2 + j][r]))
                    Gj2.append(func[1 - sgn](g[j][i], g[n2 + j][r]))

                F1.append(Fj1)
                F2.append(Fj2)
                G1.append(Gj1)
                G2.append(Gj2)

            # Next, we recursively multiply F1 and G1, and also F2 and G2
            H1 = qring_mul(F1, G1, e2)
            H2 = qring_mul(F2, G2, m + e2)

            h1, h2 = [], []
            for j in range(0, n2, 1):

                h1j, h2j = [], []
                for i in range(0, m, 1):

                    q, r = divmod(i - m + e2, m)
                    sgn = q % 2
                    h1j.append((H1[j][i] + H2[j][i]))
                    h2j.append(func[1 - sgn](0, (H1[j][r] - H2[j][r])))

                h1.append(h1j)
                h2.append(h2j)

            return h1 + h2

    if maxdeg != None:

        def poly_mul(f, flen, g, glen):

            ff = list(f) + [0] * (glen - flen)
            gg = list(g) + [0] * (flen - glen)
            # assert(len(ff) == len(gg))
            hlen = len(ff)

            # hlen > 0 implies `fast` multiplication in fp[x]/(x^n + 1) will be always used
            if hlen > mindeg:
                n = 2**len(bin(2 * hlen - 1)[2:])
                fg_qring = qring_mul([ff + [0] * (n - hlen)],
                                     [gg + [0] * (n - hlen)], 0)

                # return [ (fg_qring[0][i], in2) for i in range(0, flen + glen, 1) ]
                return fg_qring[0][:(flen + glen - 1)]

            else:
                # This branch is only for checking if karatsuba is better(?)
                return karatsuba_mul(f, flen, g, glen)
    else:

        def poly_mul(f, flen, g, glen):
            return karatsuba_mul(f, flen, g, glen)

    def poly_mul_modxn(n, f, flen, g, glen):
        """
        Next function computes f*g mod x^n but using the same idea from the C-code
        implementation of https://eprint.iacr.org/2020/341
        """

        if flen < glen:
            return poly_mul_modxn(n, g, glen, f, flen)

        # ------------------------------------
        # At this step, we ensure flen >= glen

        # We require f mod x^n and g mod x^n
        if glen > n:
            return poly_mul_modxn(n, f[:n], n, g[:n], n)

        if flen > n:
            return poly_mul_modxn(n, f[:n], n, g, glen)

        if n == 0:
            # This case return zero as the empty list, that is []
            return []

        if [] == g or [] == f:

            # Multiplication by cero (we need to ensure the output has length n, the zero polynomial corresponds a list with n zeroes
            return [0] * n

        if n == 1:
            # Only the constant coefficients are required
            return [(f[0] * g[0])]

        if n >= (flen + glen - 1):

            # Karatsuba multiplication with no possible savings
            return poly_mul(f, flen, g, glen) + [0] * (n - flen - glen + 1)

        # At this step we ensure n <  (flen + glen - 1)
        if glen == 1:

            return [(f[i] * g[0]) for i in range(0, n, 1)]

        # Here, flen >= glen >= 2
        if n == 2:

            # Multiplication of two linear polynomials modulo x^2
            # And thus, the cuadratic coefficient is not required
            f0g0 = (f[0] * g[0])
            f0g1 = (f[0] * g[1])
            f1g0 = (f[1] * g[0])
            return [f0g0, (f0g1 + f1g0)]

        if n == 3:

            if glen == 2:

                # Multiplication modulo x^3 of a linear polynomial with another of degree at least 2
                f0g0 = (f[0] * g[0])
                f1g1 = (f[1] * g[1])

                f01 = (f[0] + f[1])
                g01 = (g[0] + g[1])
                t01 = (f01 * g01)
                t01 = (t01 - f0g0)
                t01 = (t01 - f1g1)

                f2g0 = (f[2] * g[0])
                return [f0g0, t01, (f1g1 + f2g0)]

            if glen == 3:
                c00 = (f[0] * g[0])  # coeff of x^0

                c11 = (f[1] * g[1])
                f01 = (f[0] + f[1])
                g01 = (g[0] + g[1])
                c01 = (f01 * g01)
                c01 = (c01 - c00)
                c01 = (c01 - c11)

                f02 = (f[0] + f[2])
                g02 = (g[0] + g[2])
                c02 = (f02 * g02)
                c22 = (f[2] * g[2])
                c02 = (c02 - c00)
                c02 = (c02 - c22)
                c02 = (c02 + c11)
                return [c00, c01, c02]

        if n == 4 and glen == 4:

            # This special case can be used as the general case it is expensive. Maybe with anoth bound for n should fine
            S = n
            for i in range(0, n // 2, 1):
                S = S + n - 1 - 2 * i

            c = list([0] * S)
            nf = n // 2  # floor of n/2
            nc = -(-n // 2)  # Ceiling of n/2

            for i in range(0, n, 1):
                c[i] = (f[i] * g[i])
            k = n
            for i in range(0, nf, 1):
                for j in range(0, n - 2 * i - 1, 1):
                    c[k] = ((f[i] + f[i + j + 1]) * (g[i] + g[i + j + 1]))
                    c[k] = (c[k] - (c[i] + c[i + j + 1]))
                    k = k + 1

            c[n - 1] = c[0]
            for i in range(1, nf, 1):
                for j in range(1, n - 2 * i, 1):
                    c[n + 2 * i - 1 + j] = (c[n + 2 * i - 1 + j] +
                                            c[(1 + i) * n - i**2 - 1 + j])

            for i in range(1, nc, 1):
                c[n + 2 * i - 1] = (c[n + 2 * i - 1] + c[i])

            delta = n - 1
            return c[delta:(n + delta)]

        # The goal is to divide the multiplication using karatsuba style multiplication
        # but saving multiplications (some higher monomials can be omited)
        l1 = n // 2  # floor(n / 2)
        l0 = n - l1  # ceil( n / 2)

        # We proceed by spliting f(x) as f0(x^2) + x * f1(x^2)
        f1len = flen // 2  # floor(flen / 2)
        f0len = flen - f1len  # ceil( flen / 2)
        f0 = [f[2 * i + 0] for i in range(0, f0len, 1)]
        f1 = [f[2 * i + 1] for i in range(0, f1len, 1)]

        # We proceed by spliting g(x) as g0(x^2) + x * g1(x^2)
        g1len = glen // 2  # floor(glen / 2)
        g0len = glen - g1len  # ceil( geln / 2)
        g0 = [g[2 * i + 0] for i in range(0, g0len, 1)]
        g1 = [g[2 * i + 1] for i in range(0, g1len, 1)]

        # Middle part like karatsuba
        f01 = [(f0[i] + f1[i]) for i in range(0, f1len, 1)] + f0[f1len:]
        g01 = [(g0[i] + g1[i]) for i in range(0, g1len, 1)] + g0[g1len:]

        # product f0 * g0 must has degree at most x^l0
        n0 = f0len + g0len - 1
        if n0 > l0:
            n0 = l0
        fg_0 = poly_mul_modxn(n0, f0, f0len, g0, g0len)

        # product f01 * g01 must has degree at most x^l1
        n01 = f0len + g0len - 1
        if n01 > l1:
            n01 = l1
        fg_01 = poly_mul_modxn(n01, f01, f0len, g01, g0len)

        # product f1 * g1 must has degree at most x^l1
        n1 = f1len + g1len - 1
        if n1 > l1:
            n1 = l1
        fg_1 = poly_mul_modxn(n1, f1, f1len, g1, g1len)

        # Computing the middle part
        fg_01 = [(fg_01[i] - fg_0[i]) for i in range(0, n01, 1)] + fg_01[n01:]
        fg_01 = [(fg_01[i] - fg_1[i]) for i in range(0, n1, 1)] + fg_01[n1:]

        # Unifying the computations
        fg = [0] * n
        for i in range(0, n0, 1):
            fg[2 * i] = fg_0[i]

        for i in range(0, n01, 1):
            fg[2 * i + 1] = fg_01[i]

        for i in range(0, n1 - 1, 1):
            fg[2 * i + 2] = (fg[2 * i + 2] + fg_1[i])

        if 2 * n1 < n:
            fg[2 * n1] = (fg[2 * n1] + fg_1[n1 - 1])

        return fg

    def quasi_poly_mul_middle(g, glen, f, flen):
        """
        Next function computes the central part polynomial of f * g mod (x^m - 1)
        Next two functions are based on the work title "The Middle Product
        Algorithm, I." by G. Hanrot. M. Quercia, and P. Zimmermann
        """

        #    if not glen == len(g):
        #        import pdb; pdb.set_trace();
        assert glen == len(g)
        assert flen == len(f)

        if glen == 0:
            return []

        if glen == 1:
            return [(g[0] * f[0])]

        glen0 = glen // 2  # floor(glen / 2)
        glen1 = glen - glen0  # ceil(glen / 2)

        A = poly_mul_middle(
            g[glen0:],
            glen1,
            [(f[i] + f[i + glen1]) for i in range(0, 2 * glen1 - 1, 1)],
            2 * glen1 - 1,
        )

        if glen % 2 == 0:
            B = poly_mul_middle(
                [(g[glen1 + i] - g[i]) for i in range(0, glen0, 1)],
                glen0,
                f[glen1:(3 * glen1 - 1)],
                2 * glen1 - 1,
            )
        else:
            B = poly_mul_middle(
                [g[glen0]] + [(g[glen1 + i] - g[i])
                              for i in range(0, glen0, 1)],
                glen1,
                f[glen1:(3 * glen1 - 1)],
                2 * glen1 - 1,
            )

        C = poly_mul_middle(
            g[:glen0],
            glen0,
            [(f[glen1 + i] + f[2 * glen1 + i])
             for i in range(0, 2 * glen0 - 1, 1)],
            2 * glen0 - 1,
        )

        assert len(A) == glen1
        assert len(B) == glen1
        assert len(C) == glen0
        return [(A[i] - B[i])
                for i in range(0, glen1, 1)] + [(C[i] + B[i])
                                                for i in range(0, glen0, 1)]

    def poly_mul_middle(g, glen, f, flen):
        """
        Next function computes the central part polynomial of f * g mod (x^m - 1)
        This functions is an extension of quasi_poly_mul_middle()
        """

        if (glen == (flen // 2)) and (flen % 2 == 0):
            # Here deg(f) = 2d - 1 and deg(g) = d - 1
            return quasi_poly_mul_middle(g, glen, f[1:] + [0], flen)

        elif (glen == ((flen // 2) + 1)) and (flen % 2 == 0):
            # Here deg(f) = 2d - 1 and deg(g) = d
            return quasi_poly_mul_middle(g, glen, [0] + f, flen + 1)

        elif (glen == ((flen // 2) + 1)) and (flen % 2 == 1):
            # Here deg(f) = 2d and deg(g) = d
            return quasi_poly_mul_middle(g, glen, f, flen)

        else:

            # Unbalanced case when glen < floor(flen/2) and ceil(flen/2) < glen
            # This case only happens when using scaled remainder trees
            if glen == 0:
                return []

            # low part
            F0len = flen - glen
            F0 = f[:F0len]
            F0 = F0[::-1]
            G = g[::-1]
            fg_low = poly_mul_modxn(glen - 1, F0, F0len, G, glen)
            fg_low = fg_low[::-1]

            # high part
            F1 = f[F0len:]
            fg_high = poly_mul_modxn(glen, F1, glen, g, glen)

            return [(fg_low[i] + fg_high[i])
                    for i in range(0, glen - 1, 1)] + [fg_high[glen - 1]]

    def poly_mul_selfreciprocal(g, glen, f, flen):
        """
        Next function computes the product of two polynomials g(x) annd f(x) such that
        g(x) = x^dg * g(1/x) and f(x) = x^df * f(1/x) where deg g = dg, and deg f = df
        """
        if glen == 0 and flen == 0:
            return []

        if glen == 1 and flen == 1:
            return [(g[0] * f[0])]

        if glen == 2 and flen == 2:

            h = [0] * 3
            h[0] = (g[0] * f[0])
            h[1] = (h[0] + h[0])
            h[2] = h[0]
            return h

        if glen == 3 and flen == 3:

            h = [0] * 5
            h[0] = (g[0] * f[0])
            h[2] = (g[1] * f[1])
            g01 = (g[0] + g[1])
            f01 = (f[0] + f[1])
            h[1] = (g01 * f01)
            h[2] = (h[2] + h[0])
            h[1] = (h[1] - h[2])
            h[2] = (h[2] + h[0])
            h[3] = h[1]
            h[4] = h[0]
            return h

        if glen == 4 and flen == 4:

            h = [0] * 7
            h[0] = (g[0] * f[0])
            h[3] = (g[1] * f[1])
            g01 = (g[0] + g[1])
            f01 = (f[0] + f[1])
            h[2] = (g01 * f01)
            h[2] = (h[2] - h[0])
            h[1] = (h[2] - h[3])
            h[3] = (h[3] + h[0])
            h[3] = (h[3] + h[3])
            h[4] = h[2]
            h[5] = h[1]
            h[6] = h[0]
            return h

        if glen == 5 and flen == 5:

            h = [0] * 9
            g10 = (g[1] - g[0])
            f01 = (f[0] - f[1])
            h[1] = (g10 * f01)
            g20 = (g[2] - g[0])
            f02 = (f[0] - f[2])
            h[2] = (g20 * f02)
            g21 = (g[2] - g[1])
            f12 = (f[1] - f[2])
            h[3] = (g21 * f12)

            h[0] = (g[0] * f[0])
            g1f1 = (g[1] * f[1])
            g2f2 = (g[2] * f[2])

            t = (g1f1 + h[0])
            h[1] = (h[1] + t)
            h[3] = (h[3] + h[1])
            h[4] = (t + g2f2)
            h[4] = (h[4] + t)
            h[2] = (h[2] + t)
            h[2] = (h[2] + g2f2)
            h[3] = (h[3] + g1f1)
            h[3] = (h[3] + g2f2)

            h[5] = h[3]
            h[6] = h[2]
            h[7] = h[1]
            h[8] = h[0]
            return h

        if glen == flen and (glen % 2) == 1:

            # Case two degree polynomials of same even degree
            len0 = (glen + 1) // 2
            len1 = glen // 2

            g0 = [0] * len0
            f0 = [0] * len0
            g1 = [0] * len1
            f1 = [0] * len1

            # We proceed by applying the same idea as in poly_mul_modxn
            # ---
            for i in range(0, len0, 1):
                g0[i] = g[2 * i]
                f0[i] = f[2 * i]

            h0 = poly_mul_selfreciprocal(g0, len0, f0, len0)

            # ---
            for i in range(0, len1, 1):
                g1[i] = g[2 * i + 1]
                f1[i] = f[2 * i + 1]

            h1 = poly_mul_selfreciprocal(g1, len1, f1, len1)

            # ---
            for i in range(0, len1, 1):
                g0[i] = (g0[i] + g1[i])
                f0[i] = (f0[i] + f1[i])

            for i in range(0, len1, 1):
                g0[i + 1] = (g0[i + 1] + g1[i])
                f0[i + 1] = (f0[i + 1] + f1[i])

            h01 = poly_mul_selfreciprocal(g0, len0, f0, len0)

            # Mixing the computations
            for i in range(0, 2 * len0 - 1, 1):
                h01[i] = (h01[i] - h0[i])

            for i in range(0, 2 * len1 - 1, 1):
                h01[i] = (h01[i] - h1[i])

            for i in range(0, 2 * len1 - 1, 1):
                h01[i + 1] = (h01[i + 1] - h1[i])

            for i in range(0, 2 * len1 - 1, 1):
                h01[i + 1] = (h01[i + 1] - h1[i])

            for i in range(0, 2 * len1 - 1, 1):
                h01[i + 2] = (h01[i + 2] - h1[i])

            for i in range(1, 2 * len0 - 1, 1):
                h01[i] = (h01[i] - h01[i - 1])

            # Unifying the computations
            hlen = 2 * glen - 1
            h = [0] * hlen

            for i in range(0, 2 * len0 - 1, 1):
                h[2 * i] = h0[i]

            for i in range(0, 2 * len0 - 2, 1):
                h[2 * i + 1] = h01[i]

            for i in range(0, 2 * len1 - 1, 1):
                h[2 * i + 2] = (h[2 * i + 2] + h1[i])

            return h

        if glen == flen and (glen % 2) == 0:

            # Case two degree polynomials of same odd degree

            half = glen // 2
            h0 = poly_mul(g[:half], half, f[:half], half)
            h1 = poly_mul(g[:half], half, f[half:], half)

            h = [0] * (2 * glen - 1)
            for i in range(0, glen - 1, 1):
                h[i] = (h[i] + h0[i])

            for i in range(0, glen - 1, 1):
                h[2 * glen - 2 - i] = (h[2 * glen - 2 - i] + h0[i])

            for i in range(0, glen - 1, 1):
                h[half + i] = (h[half + i] + h1[i])

            for i in range(0, glen - 1, 1):
                h[glen + half - 2 - i] = (h[glen + half - 2 - i] + h1[i])

            return h

        # General case
        hlen = glen + flen - 1
        m = (hlen + 1) // 2
        h = poly_mul_modxn(m, g, glen, f, flen)
        h = h + [0] * (hlen - m)
        for i in range(m, hlen, 1):
            h[i] = h[hlen - 1 - i]

        return h

    def product_tree(f, n):
        """
        Product tree of given list of polynomials
        """

        if n == 0:
            return {'left': None, 'right': None, 'poly': [1], 'deg': 0}

        if n == 1:

            # No multiplication is required
            return {
                'left': None,
                'right': None,
                'poly': f[0],
                'deg': len(f[0]) - 1,
            }

        else:

            m = n - (n // 2)
            left = product_tree(f[:m], m)
            right = product_tree(f[m:], n - m)
            return {
                'left':
                left,
                'right':
                right,
                'poly':
                poly_mul(
                    left['poly'],
                    left['deg'] + 1,
                    right['poly'],
                    right['deg'] + 1,
                ),
                'deg':
                left['deg'] + right['deg'],
            }

    def product_selfreciprocal_tree(f, n):
        """
        Product tree of given list of self reciprocal polynomials
        """

        if n == 0:
            return {'left': None, 'right': None, 'poly': [1], 'deg': 0}

        if n == 1:

            # No multiplication is required
            return {
                'left': None,
                'right': None,
                'poly': f[0],
                'deg': len(f[0]) - 1,
            }

        else:

            m = n - (n // 2)
            left = product_selfreciprocal_tree(f[:m], m)
            right = product_selfreciprocal_tree(f[m:], n - m)
            return {
                'left':
                left,
                'right':
                right,
                'poly':
                poly_mul_selfreciprocal(
                    left['poly'],
                    left['deg'] + 1,
                    right['poly'],
                    right['deg'] + 1,
                ),
                'deg':
                left['deg'] + right['deg'],
            }

            # Now, the resultant of two polinomials corresponds with product of all g(x) mod f_i(x) when each f_i is a linear polynomial

    def product(list_g_mod_f, n):

        # When finishing tests, remove the below asserts
        if n == 0:
            return 1

        assert len(list_g_mod_f) == n

        out = list_g_mod_f[0][0]
        for j in range(1, n, 1):

            out = (out * list_g_mod_f[j][0])

        return out

    return attrdict(locals())
示例#12
0
文件: test.py 项目: adelapie/sibc
def csidh_test(ctx):
    """ GF(p)-operation cost of kps, xisog, and xeval """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    generators = algo.curve.generators
    coeff = algo.curve.coeff
    p = algo.params.p
    L = algo.params.L
    n = algo.params.n
    A = [algo.curve.field(2), algo.curve.field(4)]
    xmul = algo.curve.xmul
    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 = algo.field.init_runtime
    kps = algo.formula.kps
    show_runtime = algo.field.show_runtime
    xisog = algo.formula.xisog
    xeval = algo.formula.xeval

    total_cost = [0, 0, 0]
    print("p := 0x%X;" % p)
    print("fp := GF(p);")
    print("P<x> := PolynomialRing(fp);")

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

    if True:

        # T_p belongs to E[pi - 1]
        # T_m belongs to E[pi + 1]
        T_p, T_m = generators(A)

    else:

        # T_m belongs to E[pi - 1]
        # T_p belongs to E[pi + 1]
        T_m, T_p = generators(A)

    assert len(L) == n
    print(
        "// Now, we proceed by performing xisog with input curve equals the output curve of the previous one experiment."
    )
    for idx in range(0, n, 1):

        # -------------------------------------------------------------
        # Random kernel point
        Tp = list(T_p)
        for i in range(idx + 1, n, 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()
        kps(Tp, A, idx)
        show_runtime("kps")
        total_cost[0] += algo.field.fpmul
        total_cost[1] += algo.field.fpsqr
        total_cost[2] += algo.field.fpadd

        # -------------------------------------------------------------
        # xisog
        init_runtime()
        B = xisog(A, idx)
        show_runtime("xisog")
        total_cost[0] += algo.field.fpmul
        total_cost[1] += algo.field.fpsqr
        total_cost[2] += algo.field.fpadd

        # -------------------------------------------------------------
        # xeval: kernel point determined by the next isogeny evaluation
        init_runtime()
        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()
        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("xeval")
        total_cost[0] += algo.field.fpmul
        total_cost[1] += algo.field.fpsqr
        total_cost[2] += algo.field.fpadd
        print("|| cost: %7d" % (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='csidh-test', **locals())
示例#13
0
def bsidh_main(ctx):
    """ Random instance example of a key-exchange """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    coeff = algo.curve.coeff
    SQR, ADD = algo.curve.SQR, algo.curve.ADD
    init_runtime_basefield = algo.basefield.init_runtime
    init_runtime_field = algo.field.init_runtime
    validate = algo.curve.issupersingular
    get_A = algo.curve.get_A
    measure = algo.curve.measure
    strategy_at_6_A = algo.strategy.strategy_at_6_A
    strategy_at_6_B = algo.strategy.strategy_at_6_B
    strategy_A = algo.strategy.strategy_A
    strategy_B = algo.strategy.strategy_B
    random_scalar_A = algo.strategy.random_scalar_A
    random_scalar_B = algo.strategy.random_scalar_B

    print(
        "// The running time is assuming S = %1.2f x M and a = %1.2f x M, and giving in millions of field operations.\n"
        % (SQR, ADD))
    ''' -------------------------------------------------------------------------------------
        Main
        ------------------------------------------------------------------------------------- '''

    # ------------------------------------------------------------------------- Alice
    print("// --- \033[0;35mAlice\033[0m")
    init_runtime_basefield()
    init_runtime_field()
    a_private = random_scalar_A()
    a_public = strategy_at_6_A(a_private)

    print(
        "// Running time (Strategy evaluation):\n\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p);"
        % (
            algo.basefield.fpmul / (10.0**6),
            algo.basefield.fpsqr / (10.0**6),
            algo.basefield.fpadd / (10.0**6),
            measure([
                algo.basefield.fpmul, algo.basefield.fpsqr,
                algo.basefield.fpadd
            ]) / (10.0**6),
        ))
    print("\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p²);" % (
        algo.field.fp2mul / (10.0**6),
        algo.field.fp2sqr / (10.0**6),
        algo.field.fp2add / (10.0**6),
        measure([algo.field.fp2mul, algo.field.fp2sqr, algo.field.fp2add]) /
        (10.0**6),
    ))
    print("sk_a := %d;" % a_private)
    print("pk_a := %s;" % coeff(a_public))

    # ------------------------------------------------------------------------- Bob
    print("\n// --- \033[0;34mBob\033[0m")
    init_runtime_basefield()
    init_runtime_field()
    b_private = random_scalar_B()
    b_public = strategy_at_6_B(b_private)

    print(
        "// Running time (Strategy evaluation):\n\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p);"
        % (
            algo.basefield.fpmul / (10.0**6),
            algo.basefield.fpsqr / (10.0**6),
            algo.basefield.fpadd / (10.0**6),
            measure([
                algo.basefield.fpmul, algo.basefield.fpsqr,
                algo.basefield.fpadd
            ]) / (10.0**6),
        ))
    print("\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p²);" % (
        algo.field.fp2mul / (10.0**6),
        algo.field.fp2sqr / (10.0**6),
        algo.field.fp2add / (10.0**6),
        measure([algo.field.fp2mul, algo.field.fp2sqr, algo.field.fp2add]) /
        (10.0**6),
    ))
    print("sk_b := %d;" % b_private)
    print("pk_b := %s;" % coeff(b_public))

    print(
        "\n// ===================== \033[0;33mSecret Sharing Computation\033[0m"
    )
    # ------------------------------------------------------------------------- Alice
    print("// --- \033[0;35mAlice\033[0m")
    init_runtime_basefield()
    init_runtime_field()
    pk_b = get_A([b_public[0], algo.field(1)],
                 [b_public[1], algo.field(1)],
                 [b_public[2], algo.field(1)])
    public_validation = validate(pk_b)
    assert public_validation

    print(
        "// Running time (supersingular test):\n\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p),"
        % (
            algo.basefield.fpmul / (10.0**6),
            algo.basefield.fpsqr / (10.0**6),
            algo.basefield.fpadd / (10.0**6),
            measure([
                algo.basefield.fpmul, algo.basefield.fpsqr,
                algo.basefield.fpadd
            ]) / (10.0**6),
        ))
    print("\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p²);" % (
        algo.field.fp2mul / (10.0**6),
        algo.field.fp2sqr / (10.0**6),
        algo.field.fp2add / (10.0**6),
        measure([algo.field.fp2mul, algo.field.fp2sqr, algo.field.fp2add]) /
        (10.0**6),
    ))

    init_runtime_basefield()
    init_runtime_field()
    ss_a = strategy_A(a_private, b_public)
    print(
        "// Running time (Strategy evaluation + supersingular test):\n\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p),"
        % (
            algo.basefield.fpmul / (10.0**6),
            algo.basefield.fpsqr / (10.0**6),
            algo.basefield.fpadd / (10.0**6),
            measure([
                algo.basefield.fpmul, algo.basefield.fpsqr,
                algo.basefield.fpadd
            ]) / (10.0**6),
        ))
    print("\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p²);" % (
        algo.field.fp2mul / (10.0**6),
        algo.field.fp2sqr / (10.0**6),
        algo.field.fp2add / (10.0**6),
        measure([algo.field.fp2mul, algo.field.fp2sqr, algo.field.fp2add]) /
        (10.0**6),
    ))
    print("ss_a := %s;\n" % coeff(ss_a))

    # ------------------------------------------------------------------------- Bob
    print("// --- \033[0;34mBob\033[0m")
    init_runtime_basefield()
    init_runtime_field()
    pk_a = get_A([a_public[0], algo.field(1)],
                 [a_public[1], algo.field(1)],
                 [a_public[2], algo.field(1)])
    public_validation = validate(pk_a)
    assert public_validation

    print(
        "// Running time (supersingular test):\n\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p),"
        % (
            algo.basefield.fpmul / (10.0**6),
            algo.basefield.fpsqr / (10.0**6),
            algo.basefield.fpadd / (10.0**6),
            measure([
                algo.basefield.fpmul, algo.basefield.fpsqr,
                algo.basefield.fpadd
            ]) / (10.0**6),
        ))
    print("\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p²)," % (
        algo.field.fp2mul / (10.0**6),
        algo.field.fp2sqr / (10.0**6),
        algo.field.fp2add / (10.0**6),
        measure([algo.field.fp2mul, algo.field.fp2sqr, algo.field.fp2add]) /
        (10.0**6),
    ))

    init_runtime_basefield()
    init_runtime_field()
    ss_b = strategy_B(b_private, a_public)

    print(
        "// Running time (Strategy evaluation + supersingular test):\n\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p),"
        % (
            algo.basefield.fpmul / (10.0**6),
            algo.basefield.fpsqr / (10.0**6),
            algo.basefield.fpadd / (10.0**6),
            measure([
                algo.basefield.fpmul, algo.basefield.fpsqr,
                algo.basefield.fpadd
            ]) / (10.0**6),
        ))
    print("\t%2.3fM + %2.3fS + %2.3fa = %2.3fM in GF(p²);" % (
        algo.field.fp2mul / (10.0**6),
        algo.field.fp2sqr / (10.0**6),
        algo.field.fp2add / (10.0**6),
        measure([algo.field.fp2mul, algo.field.fp2sqr, algo.field.fp2add]) /
        (10.0**6),
    ))
    print("ss_b := %s;" % coeff(ss_b))

    try:
        assert (coeff(ss_a) == coeff(ss_b))
        print('\n\x1b[0;30;43m' + 'Successfully passed!' + '\x1b[0m')
    except:
        raise TypeError('\x1b[0;30;41m' +
                        'Great Scott!... The sky is falling. NOT PASSED!!!' +
                        '\x1b[0m')

    return attrdict(name='bsidh-main', **locals())
示例#14
0
def csidh_sdacs(ctx):
    """ SDACs as C-code headers files """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    L = algo.params.L
    n = algo.params.n
    m = algo.params.m
    exponent_of_two = (len(bin(algo.curve.cofactor)[2:]) - 1)
    validation_stop = algo.curve.validation_stop
    SDACS = algo.curve.SDACS

    # ---
    k = 3  # Number of rows (format of the list)
    # ---

    print("#ifndef _SDACS_%s_H_" % setting.prime)
    print("#define _SDACS_%s_H_" % setting.prime)
    print("")
    assert n == len(L)

    if (2**exponent_of_two ) == algo.curve.cofactor:
        print(
            "#define cofactor %d\t// Exponent of 2 in the factorization of (p + 1) "
            % exponent_of_two
        )
        print("")
    print("#ifdef _MONT_C_CODE_")

    print(
        "#define UPPER_BOUND %d\t// Bits of 4 * sqrt( [p + 1] / [2^e] )"
        % validation_stop
    )
    print(
        "\n// Recall, the list of Small Odd Primes (SOPs) is stored such that l_0 < l_1 < ... < l_{n-1}"
    )

    assert n == len(SDACS)
    print(
        "\n// The list of Shortest Differential Addition Chains (SDACs) corresponding with each l_i"
    )
    printl(
        "static int LENGTHS[]", [len(sdac_i) for sdac_i in SDACS], n // k + 1
    )
    print("")
    for i in range(0, n, 1):
        if len(SDACS[i]) == 0:
            print("static char SDAC%d[1];" % i)
        else:
            print(
                'static char SDAC'
                + str(i)
                + '[] = \"'
                + ''.join([str(sdac_ij) for sdac_ij in SDACS[i]])
                + '\";'
            )

    SDACS_string = "static char *SDACs[N] = {\n\t"

    for i in range(0, n - 1, 1):
        if (i + 1) % (n // k + 1) == 0:
            SDACS_string = SDACS_string + "SDAC%d, \n\t" % (i)
        else:
            SDACS_string = SDACS_string + "SDAC%d, " % (i)

    SDACS_string = SDACS_string + "SDAC%d\n\t};" % (n - 1)
    print("")
    print(SDACS_string)
    print("#endif")
    print(
        "\n#endif /* required framework for the SDACs, which is used in CSIDH-%s */"
        % setting.prime[1:]
    )
    return attrdict(name='csidh-sdacs', **locals())
示例#15
0
文件: polyredc.py 项目: adelapie/sibc
def PolyRedc(polymul):
    poly_mul_middle= polymul.poly_mul_middle
    poly_mul_modxn = polymul.poly_mul_modxn

    def reciprocal(f, flen, n):
        """
        This function computes the reciprocal of a given polynomial
        """
        if flen < n:
            # truncated reciprocal
            return reciprocal(f + [0] * (n - flen), n, n)

        if n == 0:
            # Super special base case
            return [1], 1

        if n == 1:

            # Base case when (1/f) mod x^n = 1
            return [1], f[0]

        elif n == 2:

            # Base case when (1/f) = f_0 - f_1*x
            return [f[0], (0 - f[1])], (f[0] ** 2)

        elif n == 3:

            g, a = reciprocal(f[:2], 2, 2)

            t0 = (f[1] ** 2)
            t1 = (f[0] * f[2])
            t2 = (t1 - t0)
            t2 = (t2 * f[0])
            return (
                [(g[0] * a), (g[1] * a), (0 - t2)],
                (a ** 2),
            )

        elif n == 4:

            # This case gives the same cost as the general case
            g, a = reciprocal(f[:2], 2, 2)
            t0 = (f[1] ** 2)
            t1 = (g[0] * f[2])
            t2 = (g[0] * f[3])
            t3 = (g[1] * f[2])
            t0 = (t1 - t0)
            t1 = (t2 + t3)
            t2 = (t0 * g[0])
            t3 = (t0 * g[1])
            t4 = (t1 * g[0])
            t3 = (t3 + t4)
            return (
                [
                    (g[0] * a),
                    (g[1] * a),
                    (0 - t2),
                    (0 - t3),
                ],
                (a ** 2),
            )

        else:

            m = n - (n // 2)  # Ceiling of n/2

            # Recursively call to reciprocal
            g, a = reciprocal(f, flen, m)

            """
            # Basic idea
            t = poly_mul_modxn(n, f[:n], n, g, m)                                   #  f * g          mod x^n
            t = [(t[0] - a) ] + t[1:n]                                              # (f * g - a)     mod x^n
            assert( [0]*m == t[:m] )                                                # the first m coefficients are equal zero
            t = poly_mul_modxn(n - m, g, m, t[m:], n - m)                           # (f * g - a) * g mod x^n

            # Finally, the reciprocal is a*g - (f*g - a)*g mod x^n
            t = [(a * g[i]) for i in range(0, m, 1) ] + [(-t[i]) for i in range(0, n - m, 1) ]
            """

            # Basic idea but saving multiplication because of f*g - a is multiple of x^m
            t = poly_mul_middle(
                g, m, f[:n], n
            )  #  f * g          mod x^n (the last 'm >= n - m' coefficients)
            t = poly_mul_modxn(
                n - m, g, m, t[(2 * m - n) :], n - m
            )  # (f * g - a) * g mod x^n

            # Finally, the reciprocal is a*g - (f*g - a)*g mod x^n
            t = [(a * g[i]) for i in range(0, m, 1)] + [
                (0 - t[i]) for i in range(0, n - m, 1)
            ]

            return t, (a ** 2)

    def poly_redc(h, hlen, f):
        """
        Modular reduction in fp[x] with precomputation
        """

        flen = f['deg'] + 1

        if hlen < flen:
            # Base case, h(x) mod f(x) = h(x)
            return list(h) + [0] * (flen - hlen - 1)

        elif flen == 2 and hlen == 2:

            t0 = (h[0] * f['poly'][1])  # h0 * f1
            t1 = (h[1] * f['poly'][0])  # h1 * f0
            return [(t0 - t1)]  # f1 * (h0 + h1*x) mod (f0 + f1*x)

        elif flen == 2 and hlen == 3:

            f0_squared = (f['poly'][0] ** 2)  # f0^2
            f1_squared = (f['poly'][1] ** 2)  # f1^2
            t = (f['poly'][0] - f['poly'][1])  # f0 - f1

            t = (t ** 2)  # (f0 - f1)^2
            t = (t - f0_squared)  # (f0 - f1)^2 - f0^2
            t = (
                t - f1_squared
            )  # (f0 - f1)^2 - f0^2 - f1^2 = -2*f0*f1

            f0_squared = (f0_squared + f0_squared)  # 2*(f0^2)
            f1_squared = (f1_squared + f1_squared)  # 2*(f1^2)

            t0 = (f0_squared * h[2])  # [2*(f0^2)] * h2
            t1 = (f1_squared * h[0])  # [2*(f1^2)] * h0
            t2 = (t * h[1])  # [-2*f0*f1] * h1
            return [
                (t0 + (t1 + t2))
            ]  # [2 * (f1^2)] * (h0 + h1*x + h2*x^2) mod (f0 + f1*x)

        elif flen == 3 and hlen == 3:

            f2h1 = (f['poly'][2] * h[1])
            f2h0 = (f['poly'][2] * h[0])
            f1h2 = (f['poly'][1] * h[2])
            f0h2 = (f['poly'][0] * h[2])
            return [(f2h0 - f0h2), (f2h1 - f1h2)]
            """
        elif flen == hlen:

            t0 = [(h[inner] * f['poly'][fdeg - 1]) for inner in range(0, fdeg - 1, 1) ]
            t1 = [(h[fdeg - 1] * f['poly'][inner]) for inner in range(0, fdeg - 1, 1) ]
            return [(t1[inner] - t0[inner]) for inner in range(0, fdeg - 1, 1) ]
            """
        else:

            H = [h[i] for i in range(hlen - 1, -1, -1)]  # x^deg(h) * h(x^-1)
            Q = list(f['reciprocal'])  # (1/F) mod x^(deg(f) - deg(h))

            # (H/F) mod x^(deg(f) - deg(h))
            Q = poly_mul_modxn(
                hlen - flen + 1,
                Q[: (hlen - flen + 1)],
                hlen - flen + 1,
                H[: (hlen - flen + 1)],
                hlen - flen + 1,
            )

            q = [
                Q[i] for i in range(hlen - flen, -1, -1)
            ]  # x^deg(Q) * Q(x^-1) is the quotient
            qf = poly_mul_modxn(
                flen - 1, q, hlen - flen + 1, f['poly'], flen
            )  # (q * f) (x) has degree equals deg(h)

            # a*h(x) - (q * f)(x) will gives a polynomial of degree (deg(f) - 1)
            return [
                ((f['a'] * h[i]) - qf[i])
                for i in range(0, flen - 1, 1)
            ]

    def reciprocal_tree(r, glen, ptree_f, n):
        """
        Reciprocal tree of a given product tree
        """
        if n == 0:
            # Super special base case (nothing to do)
            return {
                'left': None,
                'right': None,
                'poly': [1],
                'deg': 0,
                'reciprocal': [1],
                'a': 1,
            }

        if n == 1:
            # We are in the leaf
            return {
                'left': None,
                'right': None,
                'poly': list(ptree_f['poly']),
                'deg': ptree_f['deg'],
                'reciprocal': [1],
                'a': 1,
            }

        if ptree_f['deg'] == 2 and (glen == 3):
            # Not required because of our redcution base cases
            return {
                'left': ptree_f['left'],
                'right': ptree_f['right'],
                'poly': list(ptree_f['poly']),
                'deg': ptree_f['deg'],
                'reciprocal': [1],
                'a': 1,
            }

        # At this point, we deal with a general case
        flen = ptree_f['deg'] + 1
        # R, A = reciprocal(ptree_f['poly'][::-1], flen, glen - flen + 1  )
        if r['rdeg'] <= (glen - flen):

            # This case requires a reciprocal computation
            R, A = reciprocal(ptree_f['poly'][::-1], flen, glen - flen + 1)

        else:

            # This case allows to use a polynomial multiplication modulo x^{glen - flen + 1}
            A = r['a']
            R = poly_mul_modxn(
                glen - flen + 1,
                r['rpoly'],
                r['rdeg'] + 1,
                r['fpoly'][::-1],
                r['fdeg'] + 1,
            )

        # Now, we proceed by recusion calls
        m = n - (n // 2)
        left = reciprocal_tree(
            {
                'rpoly': R,
                'rdeg': (glen - flen),
                'fpoly': ptree_f['right']['poly'],
                'fdeg': ptree_f['right']['deg'],
                'a': A,
            },
            flen - 1,
            ptree_f['left'],
            m,
        )
        right = reciprocal_tree(
            {
                'rpoly': R,
                'rdeg': (glen - flen),
                'fpoly': ptree_f['left']['poly'],
                'fdeg': ptree_f['left']['deg'],
                'a': A,
            },
            flen - 1,
            ptree_f['right'],
            n - m,
        )

        return {
            'left': left,
            'right': right,
            'poly': list(ptree_f['poly']),
            'deg': ptree_f['deg'],
            'reciprocal': R,
            'a': A,
        }

    def multieval_unscaled(g, glen, ptree_f, n):
        """
        Next function computes g(x) mod f_1(x), ..., g(x) mod f_n(x)
        """

        if n == 0:
            return [[1]]

        g_mod = poly_redc(g, glen, ptree_f)

        if n == 1:

            # Now, we have g corresponds with the initial G but now it is modulus a leaf of the product tree of f
            return [g_mod]

        else:

            m = n - (n // 2)
            # Reducing g(x) modulo the current node polynomial
            left = multieval_unscaled(
                g_mod, ptree_f['deg'], ptree_f['left'], m
            )
            right = multieval_unscaled(
                g_mod, ptree_f['deg'], ptree_f['right'], n - m
            )
            return left + right

    def multieval_scaled(g, glen, f, flen, ptree_f, n):
        """
        Next functions computes the scaled remainder tree
        """

        if n == 0:
            return [[1]]

        # fg = poly_mul_middle(f, flen, g, glen)
        if flen == n and glen == n and n > 1:
            fg = list(g)
        else:
            fg = poly_mul_middle(f, flen, g, glen)

        if n == 1:
            # The last coefficient should be the desire modular reduction with linear modulus
            if fg != []:
                return [[fg[-1]]]
            else:
                return [[1]]

        m = n - (n // 2)
        left = multieval_scaled(
            fg,
            flen,
            ptree_f['right']['poly'],
            ptree_f['right']['deg'] + 1,
            ptree_f['left'],
            m,
        )
        right = multieval_scaled(
            fg,
            flen,
            ptree_f['left']['poly'],
            ptree_f['left']['deg'] + 1,
            ptree_f['right'],
            n - m,
        )
        return left + right

    return attrdict(locals())
示例#16
0
文件: test.py 项目: adelapie/sibc
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())
示例#17
0
文件: main.py 项目: adelapie/sibc
def csidh_main(ctx):
    """ Random instance example of a key-exchange """
    algo = ctx.meta['sibc.kwargs']['algo']
    setting = ctx.meta['sibc.kwargs']
    coeff = algo.curve.coeff
    p = algo.params.p
    L = algo.params.L
    m = algo.params.m
    n = algo.params.n
    SQR, ADD = algo.curve.SQR, algo.curve.ADD
    init_runtime = algo.field.init_runtime
    validate = algo.curve.issupersingular
    measure = algo.curve.measure
    GAE_at_0 = algo.gae.GAE_at_0
    GAE_at_A = algo.gae.GAE_at_A
    random_exponents = algo.gae.random_exponents
    print_exponents = algo.gae.print_exponents

    print(
        "// The running time is assuming S = %1.2f x M and a = %1.2f x M, and giving in millions of field operations.\n"
        % (SQR, ADD)
    )

    ''' -------------------------------------------------------------------------------------
        Main
        ------------------------------------------------------------------------------------- '''
    print_exponents("// Exponent bounds", m)
    print("\n// ===================== \033[0;33mPublic Key Generation\033[0m")

    # ------------------------------------------------------------------------- Alice
    print("// --- \033[0;35mAlice\033[0m")
    init_runtime()
    a_private = random_exponents(m)
    a_public = GAE_at_0(a_private)

    print(
        "// Running time (GAE):\t\t\t%2.3fM + %2.3fS + %2.3fa = %2.3fM;"
        % (
            algo.field.fpmul / (10.0 ** 6),
            algo.field.fpsqr / (10.0 ** 6),
            algo.field.fpadd / (10.0 ** 6),
            measure([algo.field.fpmul, algo.field.fpsqr, algo.field.fpadd]) / (10.0 ** 6),
        )
    )
    print_exponents("sk_a", a_private)
    print("pk_a := %s;" % coeff(a_public))

    # ------------------------------------------------------------------------- Bob
    print("\n// --- \033[0;34mBob\033[0m")
    init_runtime()
    b_private = random_exponents(m)
    b_public = GAE_at_0(b_private)
    
    print(
        "// Running time (GAE):\t\t\t%2.3fM + %2.3fS + %2.3fa = %2.3fM;"
        % (
            algo.field.fpmul / (10.0 ** 6),
            algo.field.fpsqr / (10.0 ** 6),
            algo.field.fpadd / (10.0 ** 6),
            measure([algo.field.fpmul, algo.field.fpsqr, algo.field.fpadd]) / (10.0 ** 6),
        )
    )
    print_exponents("sk_b", b_private)
    print("pk_b := %s;" % coeff(b_public))

    print("\n// ===================== \033[0;33mSecret Sharing Computation\033[0m")
    # ------------------------------------------------------------------------- Alice
    print("// --- \033[0;35mAlice\033[0m")
    init_runtime()
    public_validation = validate(b_public)
    assert public_validation
    
    print(
        "// Running time (key validation):\t%2.3fM + %2.3fS + %2.3fa = %2.3fM,"
        % (
            algo.field.fpmul / (10.0 ** 6),
            algo.field.fpsqr / (10.0 ** 6),
            algo.field.fpadd / (10.0 ** 6),
            measure([algo.field.fpmul, algo.field.fpsqr, algo.field.fpadd]) / (10.0 ** 6),
        )
    )

    init_runtime()
    ss_a = GAE_at_A(a_private, b_public)
    print(
        "// Running time (GAE + key validation):\t%2.3fM + %2.3fS + %2.3fa = %2.3fM;"
        % (
            algo.field.fpmul / (10.0 ** 6),
            algo.field.fpsqr / (10.0 ** 6),
            algo.field.fpadd / (10.0 ** 6),
            measure([algo.field.fpmul, algo.field.fpsqr, algo.field.fpadd]) / (10.0 ** 6),
        )
    )
    print("ss_a := %s;\n" % coeff(ss_a))

    # ------------------------------------------------------------------------- Bob
    print("// --- \033[0;34mBob\033[0m")
    init_runtime()
    public_validation = validate(a_public)
    assert public_validation
    
    print(
        "// Running time (key validation):\t%2.3fM + %2.3fS + %2.3fa = %2.3fM,"
        % (
            algo.field.fpmul / (10.0 ** 6),
            algo.field.fpsqr / (10.0 ** 6),
            algo.field.fpadd / (10.0 ** 6),
            measure([algo.field.fpmul, algo.field.fpsqr, algo.field.fpadd]) / (10.0 ** 6),
        )
    )

    init_runtime()
    ss_b = GAE_at_A(b_private, a_public)
    
    print(
        "// Running time (GAE + key validation):\t%2.3fM + %2.3fS + %2.3fa = %2.3fM;"
        % (
            algo.field.fpmul / (10.0 ** 6),
            algo.field.fpsqr / (10.0 ** 6),
            algo.field.fpadd / (10.0 ** 6),
            measure([algo.field.fpmul, algo.field.fpsqr, algo.field.fpadd]) / (10.0 ** 6),
        )
    )
    print("ss_b := %s;" % coeff(ss_b))

    try:
        assert(coeff(ss_a) == coeff(ss_b))
        print('\n\x1b[0;30;43m' + 'Successfully passed!' + '\x1b[0m')
    except:
        raise TypeError(
            '\x1b[0;30;41m'
            + 'Great Scott!... The sky is falling. NOT PASSED!!!'
            + '\x1b[0m'
        )

    return attrdict(name='csidh-main', **locals())
示例#18
0
def MontgomeryCurve(prime):

    name = prime
    model= 'montgomery'
    if prime in parameters['csidh'].keys():
        # CSIDH only requires the factorization of p + 1
        L = parameters['csidh'][prime]['L']
        n = parameters['csidh'][prime]['n']
        cofactor = parameters['csidh'][prime]['cofactor']
        p = parameters['csidh'][prime]['p']
        p_minus_one_halves = parameters['csidh'][prime]['p_minus_one_halves']
        validation_stop = sum([bitlength(l_i) for l_i in L]) / 2.0 + 2
        field = PrimeField(p)

    elif prime in parameters['bsidh'].keys():
        # B-SIDH only requires the factorization of p + 1 and p - 1
        nm = parameters['bsidh'][prime]['nm']
        np = parameters['bsidh'][prime]['np']
        Lp = parameters['bsidh'][prime]['Lp']
        Lm = parameters['bsidh'][prime]['Lm']
        Ep = parameters['bsidh'][prime]['Ep']
        Em = parameters['bsidh'][prime]['Em']
        L = list(Lp + Lm)
        n = len(L)
        p = parameters['bsidh'][prime]['p']
        cofactor_p = (p + 1) // reduce(lambda x, y: (x * y), Lp)
        cofactor_m = (p - 1) // reduce(lambda x, y: (x * y), Lm)
        validation_stop = sum([bitlength(l_i) for l_i in Lp]) / 2.0 + 2
        p_minus_one_halves = parameters['bsidh'][prime]['p_minus_one_halves']
        p_minus_3_quarters = parameters['bsidh'][prime]['p_minus_3_quarters']
        field = QuadraticField(p)
    else:
        assert False, "only CSIDH and B-SIDH are currently implemented"

    # Shortest Differential Addition Chains (SDACs) for each l_i
    path = resource_filename('sibc', "data/sdacs/" + prime)
    SDACS = filename_to_list_of_lists_of_ints(path)
    assert len(SDACS) > 0, f'There is file precomputed sdacs for {prime} prime'
    SDACS_LENGTH = list(map(len, SDACS))

    cmul = lambda l: numpy.array(
        [
            4.0 * (SDACS_LENGTH[L.index(l)] + 2),
            2.0 * (SDACS_LENGTH[L.index(l)] + 2),
            6.0 * (SDACS_LENGTH[L.index(l)] + 2) - 2.0,
        ]
    )
    c_xmul = list(map(cmul, L))  # list of the costs of each [l]P

    SQR = 1.00
    ADD = 0.00
    def measure(x):
        """
        Field additions, multiplications, and squarings
        SQR = 1.00
        # In F_p, we have SQR_{F_p} = SQR x MUL_{F_p}
        ADD = 0.00
        # In F_p, we have ADD_{F_p} = ADD x MUL_{F_p}
        """
        return x[0] + SQR * x[1] + ADD * x[2]

    random = SystemRandom()

    def jinvariant(A):
        """
        -------------------------------------------------------------------------
        jinvariant()
        input : projective Montgomery constants A24 := A + 2C and C24 := 4C where
                E : y^2 = x^3 + (A/C)*x^2 + x
        output: the j-invariant of E
        -------------------------------------------------------------------------
        """
        A4_squared = (A[0] + A[0])              # (2 * A24)
        A4_squared = (A4_squared - A[1])        # (2 * A24) - C24
        A4_squared = (A4_squared + A4_squared)  # 4*A = 2[(2 * A24) - C24]

        # Now, we have A = A' / C' := A4 / A[1] = (4*A) / (4*C)
        A4_squared = (A4_squared ** 2)  # (A')^2
        C4_squared = (A[1] ** 2)        # (C')^2
        t = (C4_squared + C4_squared)   # 2 * [(C')^2]

        num = (C4_squared + t)      # 3 * [(C')^2]
        num = (A4_squared - num)    # (A')^2 - 3 * [(C')^2]
        s = (num ** 2)              # { (A')^2 - 3 * [(C')^2] }^2
        num = (num * s)             # { (A')^2 - 3 * [(C')^2] }^3

        C4_squared = (C4_squared ** 2)  # (C')^4
        den = (t + t)                   # 4 * [(C')^2]
        den = (A4_squared - den)        # (A')^2 - 4 * [(C')^2]
        den = (den * C4_squared)        # {(A')^2 - 4 * [(C')^2] } * [(C')^4]
        den = (den ** -1)               # 1 / {(A')^2 - 4 * [(C')^2] } * [(C')^4]

        num = (num * den)  # j := { (A')^2 - 3 * [(C')^2] }^3 / {(A')^2 - 4 * [(C')^2] } * [(C')^4]
        num = (num + num)  #   2*j
        num = (num + num)  #   4*j
        num = (num + num)  #   8*j
        num = (num + num)  #  16*j
        num = (num + num)  #  32*j
        num = (num + num)  #  64*j
        num = (num + num)  # 128*j
        num = (num + num)  # 256*j
        return num

    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],
        )

    def affine_to_projective(affine):
        """
        affine_to_projective()
        input : the affine Montgomery coefficient A=A'/C with C=1
        output: projective Montgomery constants A24 := A' + 2C and C24 := 4C
                where E : y^2 = x^3 + (A'/C)*x^2 + x
        """
        return [affine + field(2), field(4)]

    def coeff(A):
        """
        ----------------------------------------------------------------------
        coeff()
        input : projective Montgomery constants A24 := A + 2C and C24 := 4C
                where E : y^2 = x^3 + (A/C)*x^2 + x
        output: the affine Montgomery coefficient A/C
        ----------------------------------------------------------------------
        """
        output = (A[0] + A[0])         # (2 * A24)
        output = (output - A[1])       # (2 * A24) - C24
        C24_inv = (A[1] ** -1)         # 1 / (C24)
        output = (output + output)     # 4*A = 2[(2 * A24) - C24]
        output = (output * C24_inv)    # A/C = 2[(2 * A24) - C24] / C24

        return output

    def get_A(P, Q, PQ):
        """
        ----------------------------------------------------------------------
        coeff()
        input : the affine Montgomery x-coordinate points x(P) := (XP : YP), 
                x(Q) := (XQ : ZQ), and x(P - Q) := (XPQ : ZPQ) on the curve 
                E : y^2 = x^3 + (A/C)*x^2 + x
        output: the projective Montgomery coefficient (A + 2C : 4C)
        ----------------------------------------------------------------------
        """
        t0 = (P[0] + P[1])   # XP + ZP
        t1 = (Q[0] + Q[1])   # XQ + ZQ

        t = (t0 * t1)        # (XP + ZP) * (XQ + ZQ)
        XPXQ = (P[0] * Q[0]) # XP * XQ
        ZPZQ = (P[1] * Q[1]) # ZP * ZQ

        t = (t - XPXQ)
        t = (t - ZPZQ)      # XPZQ + ZPXQ
        s = (XPXQ - ZPZQ)   # XPXQ - ZPZQ

        t0 = (t *  PQ[0])   # (XPZQ + ZPXQ) * XPQ
        t1 = (s *  PQ[1])   # (XPXQ - ZPZQ) * ZPQ
        t0 = (t0 + t1)      # (XPZQ + ZPXQ) * XPQ + (XPXQ - ZPZQ) * ZPQ
        t0 = (t0 ** 2)      # [(XPZQ + ZPXQ) * XPQ + (XPXQ - ZPZQ) * ZPQ] ^ 2

        t1 = (t * PQ[1])    # (XPZQ + ZPXQ) * ZPQ
        s = (ZPZQ * PQ[0])  # ZPZQ * XPQ
        t1 = (t1 + s)       # (XPZQ + ZPXQ) * ZPQ + ZPZQ * XPQ
        s = (XPXQ * PQ[0])  # (XPXQ) * XPQ
        s = (s + s)         # 2 * [(XPXQ) * XPQ]
        s = (s + s)         # 4 * [(XPXQ) * XPQ]
        t1 = (t1 * s)       # [(XPZQ + ZPXQ) * ZPQ + ZPZQ * XPQ] * (4 * [(XPXQ) * XPQ])

        t = (ZPZQ * PQ[1])  # ZPZQ * ZPQ

        XPXQ = (t0 - t1)    # [(XPZQ + ZPXQ) * XPQ + (XPXQ - ZPZQ) * ZPQ] ^ 2 - [(XPZQ + ZPXQ) * ZPQ + ZPZQ * XPQ] * (4 * [(XPXQ) * XPQ])
        ZPZQ = (s * t)      # (4 * [(XPXQ) * XPQ]) * (ZPZQ * ZPQ)

        # ---
        B1 = (ZPZQ + ZPZQ)  # 2C
        B0 = (XPXQ + B1)    # A + 2C
        B1 = (B1 + B1)      # 4C
        return [B0, B1]

    def isinfinity(P):
        """ isinfinity(P) determines if x(P) := (XP : ZP) = (1 : 0) """
        return P[1] == 0

    def isequal(P, Q):
        """ isequal(P, Q) determines if x(P) = x(Q) """
        return (P[0] * Q[1]) == (P[1] * Q[0])

    def xdbl(P, A):
        """
        ----------------------------------------------------------------------
        xdbl()
        input : a projective Montgomery x-coordinate point x(P) := XP/ZP, and
                the  projective Montgomery constants A24:= A + 2C and C24:=4C
                where E : y^2 = x^3 + (A/C)*x^2 + x
        output: the projective Montgomery x-coordinate point x([2]P)
        ----------------------------------------------------------------------
        """
        t_0 = (P[0] - P[1])
        t_1 = (P[0] + P[1])
        t_0 = (t_0 ** 2)
        t_1 = (t_1 ** 2)
        Z = (A[1] * t_0)
        X = (Z * t_1)
        t_1 = (t_1 - t_0)
        t_0 = (A[0] * t_1)
        Z = (Z + t_0)
        Z = (Z * t_1)

        return [X, Z]

    def xadd(P, Q, PQ):
        """
        ----------------------------------------------------------------------
        xadd()
        input : the projective Montgomery x-coordinate points x(P) := XP/ZP,
                x(Q) := XQ/ZQ, and x(P-Q) := XPQ/ZPQ
        output: the projective Montgomery x-coordinate point x(P+Q)
        ----------------------------------------------------------------------
        """
        a = (P[0] + P[1])
        b = (P[0] - P[1])
        c = (Q[0] + Q[1])
        d = (Q[0] - Q[1])
        a = (a * d)
        b = (b * c)
        c = (a + b)
        d = (a - b)
        c = (c ** 2)
        d = (d ** 2)
        X = (PQ[1] * c)
        Z = (PQ[0] * d)
        return [X, Z]

    def xdbladd(P, Q, PQ, A):
        """
        ----------------------------------------------------------------------
        xdbladd()
        input : a projective Montgomery x-coordinate point x(P) := XP/ZP, x(Q) := XQ/ZQ, 
                and x(P-Q) := XPQ/ZPQ, and the projective Montgomery constant (a24 : 1) 
                = (A + 2C: 4C)  where E : y^2 = x^3 + (A/C)x^2 + x
        output: the projective Montgomery x-coordinate point x([2]P), x([P+Q])
        ----------------------------------------------------------------------
        """
        #T = xdbl(P, A)
        #S = xadd(P, Q, PQ)
        #return T, S
        t0 = (P[0] + P[1])
        t1 = (P[0] - P[1])
        X2 = (t0 ** 2)
        t2 = (Q[0] - Q[1])
        X3 = (Q[0] + Q[1])
        t0 = (t0 * t2)
        Z2 = (t1 ** 2)
        # ---
        t1 = (t1 * X3)
        t2 = (X2 - Z2)
        X2 = (X2 * Z2)
        X3 = (A * t2)
        Z3 = (t0 - t1)
        Z2 = (X3 + Z2)
        X3 = (t0 + t1)
        # ---
        Z2 = (Z2 * t2)
        Z3 = (Z3 ** 2)
        X3 = (X3 ** 2)
        Z3 = (PQ[0] * Z3)
        X3 = (PQ[1] * X3)
        return [X2, Z2], [X3, Z3]

    def xtpl(P, A):
        """
        ----------------------------------------------------------------------
        xtpl()
        input : a projective Montgomery x-coordinate point x(P) := XP/ZP, and
                the  projective Montgomery constants A24:= A + 2C and C24:=4C
                where E : y^2 = x^3 + (A/C)*x^2 + x
        output: the projective Montgomery x-coordinate point x([3]P)
        ----------------------------------------------------------------------
        """
        # A - 2C
        C = A[0] - A[1]
        # ---
        t0 = (P[0] - P[1])
        t2 = (t0 ** 2)
        t1 = (P[0] + P[1])
        t3 = (t1 ** 2)
        t4 = (t1 + t0)
        t0 = (t1 - t0)
        # ---
        t1 = (t4 ** 2)
        t1 = (t1 - t3)
        t1 = (t1 - t2)
        t5 = (t3 * A[0])
        t3 = (t5 * t3)
        t6 = (t2 * C)
        # ---
        t2 = (t2 * t6)
        t3 = (t2 - t3)
        t2 = (t5 - t6)
        t1 = (t2 * t1)
        t2 = (t3 + t1)
        t2 = (t2 ** 2)
        # ---
        X = (t2 * t4)
        t1 = (t3 - t1)
        t1 = (t1 ** 2)
        Z = (t1 * t0)
        return [X,Z]

    def xmul(P, A, j):
        """
        ----------------------------------------------------------------------
        xmul()
        input : a projective Montgomery x-coordinate point x(P) := XP/ZP, the
                projective Montgomery constants A24:= A + 2C and C24:=4C where
                E : y^2 = x^3 + (A/C)*x^2 + x, and an positive integer j
        output: the projective Montgomery x-coordinate point x([L[j]]P)
        ----------------------------------------------------------------------
        """
        P2 = xdbl(P, A)
        R = [P, P2, xadd(P2, P, P)]

        for i in range(SDACS_LENGTH[j] - 1, -1, -1):

            if isinfinity(R[SDACS[j][i]]):
                T = xdbl(R[2], A)
            else:
                T = xadd(R[2], R[SDACS[j][i] ^ 1], R[SDACS[j][i]])

            R[0] = list(R[SDACS[j][i] ^ 1])
            R[1] = list(R[2])
            R[2] = list(T)

        return R[2]

    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

    # Golden ration is used in prac algorithm
    phi = (1.0 + sqrt(0.5)) / 2.0
    phi_nu, phi_de = phi.as_integer_ratio()
    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

    def prac(k, P, A):
        """ PRAC algorithm: (simplified) 1-D Euclidean pseudomultiplication """

        s = k
        infty = [field(1), field(0)]  # Point at infinity

        # Reducing the problem from k = 2^i x s to s
        x = list(P)
        while s % 2 == 0:
            x = xdbl(x, A)
            s //= 2

        r = (s * phi_nu) // phi_de
        x = euclid2d(r, s - r, x, x, infty, A)
        return x

    def cofactor_multiples(P, A, points):
        """
        ----------------------------------------------------------------------
        cofactor_multiples()
        input : a projective Montgomery x-coordinate point x(P) := XP/ZP, the
                projective Montgomery constants A24:= A + 2C and C24:=4C where
                E : y^2 = x^3 + (A/C)*x^2 + x, and subset of |[0, n]|
        output: the projective Montgomery x-coordinate points x([(p+1) / l_0]P),
                x([(p+1) / l_1]P), ..., x([(p+1) / l_{n-1}]P).
        ----------------------------------------------------------------------
        """
        n = len(points)
        if n == 1:
            # In this recursion level we have an order-l point
            return [P]
        elif n > 0:
            # We proceed by applying a divide-and-conquer procedure
            h = n // 2
            if h > 0:

                # 1st half
                first_half = []
                second_P = P
                for j in range(h):
                    second_P = xmul(second_P, A, points[j])
                    first_half.append(points[j])

                # 2nd half
                second_half = []
                first_P = P
                for j in range(h, n):
                    first_P = xmul(first_P, A, points[j])
                    second_half.append(points[j])

                return cofactor_multiples(first_P, A, first_half) + cofactor_multiples(
                    second_P, A, second_half
                )

            return []

    def isfullorder(seq):
        """ isfullorder() checks if the point at infinity belongs or not to a given list """
        tmp = [not isinfinity(seq_i) for seq_i in seq]
        return reduce(lambda x, y: (x and y), tmp)

    def generators(A):
        """ generators() looks for two full-order poinst on E[pi + 1] or E[pi - 1] """
        output = [[0, 0], [0, 0]]
        while [0, 0] in output:

            T_p, T_m = elligator(A)
            T_p = prac(cofactor, T_p, A)
            if isfullorder(cofactor_multiples(T_p, A, range(0, n, 1))) and output[
                0
            ] == [0, 0]:
                output[0] = list(T_p)

            T_m = prac(cofactor, T_m, A)
            if isfullorder(cofactor_multiples(T_m, A, range(0, n, 1))) and output[
                1
            ] == [0, 0]:
                output[1] = list(T_m)

        return output[0], output[1]

    def crisscross(alpha, beta, gamma, delta):
        """ crisscross() computes a*c + b*d, and a*c - b*d """
        t_1 = (alpha * delta)
        t_2 = (beta * gamma)
        return (t_1 + t_2), (t_1 - t_2)

    if prime in parameters['csidh'].keys():
        def issupersingular(A):
            """ issupersingular() verifies supersingularity """
            while True:

                T_p, _ = elligator(A) # T_p is always in GF(p), and thus has torsion (p+1)
                T_p = prac(cofactor, T_p, A)
                P = cofactor_multiples(T_p, A, range(0, n, 1))

                bits_of_the_order = 0
                for i in range(0, n, 1):

                    if isinfinity(P[i]) == False:

                        Q = xmul(P[i], A, i)

                        if isinfinity(Q) == False:
                            return False

                        bits_of_the_order += bitlength(L[i])
                        if bits_of_the_order > validation_stop:
                            return True

    elif prime in parameters['bsidh'].keys():
        def issupersingular(A):
            """ issupersingular() verifies supersingularity """
            while True:

                P, _ = elligator(A) # T_p is always in GF(p²)

                # Is P a torsion-(p + 1)?
                T_p = prac(cofactor_p, P, A)
                Tp = cofactor_multiples(T_p, A, range(0, np, 1))
                Tp = [xmul(Tp[i], A, i) for i in range(0, np, 1)]
                Tp = [isinfinity(Tp_i) for Tp_i in Tp]

                # Is P a torsion-(p - 1)?
                T_m = prac(cofactor_m, P, A)
                Tm = cofactor_multiples(T_m, A, range(np, n, 1))
                Tm = [xmul(Tm[i - np], A, i) for i in range(np, n, 1)]
                Tm = [isinfinity(Tm_i) for Tm_i in Tm]

                return reduce(lambda x, y: (x and y), Tp) or reduce(lambda x, y: (x and y), Tm)
    else:
        assert False, "only CSIDH and B-SIDH are currently implemented"

    return attrdict(**locals())
示例#19
0
class known_df_p512(object):

    """
    Known-answer tests

    We currently only have these for p512 df keys.

    These keys and answers came from running csidh/main.py from 
    https://github.com/JJChiDguez/csidh_withstrategies (which
    generated these keys).

    To run this test for just one formula, you can run commands like this:
        pytest -k 'known_df and hvelu'
    """

    keys = attrdict(
        A=attrdict(
            sk=[7, -14, -16, -5, 7, -4, 6, -16, 14, 22, -10, 5, 16, 8, -17, -1, -1, -11, 5, 11, 1, -1, 7, -5, 10, -2, 14, -13, 12, -4, -11, 10, -9, -9, 5, 9, 1, 9, 9, 0, 6, -4, 6, -5, 4, -6, 2, 4, 6, 3, 0, -2, 0, -6, -5, -3, 5, 5, -3, 5, 1, -1, 3, -1, 4, -2, 0, 0, 2, 4, 0, 4, 4, 1],
            pk=[4728146032486912801094583991353286261973546366143541397245686753762901316062339836438041892681193229023769378118684082801187071058443826977747221505877393, 2983658557129372264342771993184706542050709103347982710108512572730239907028070100828017140583214399358736276490324811325106128107134002761476490169757932],
            compressed=0x3BD2E2F7B2EA49932420DAF81189C350519FF0E29589587C0344111D4822C80AD88100DAC094898DE9FF615A61929B2A6D23C1C149CB031E47AE3BC4E4DC6A19,
        ),
        B=attrdict(
            sk=[3, 14, 18, -15, 17, 22, -12, -14, -20, 10, 10, 3, 10, -8, -17, 13, -15, 19, -19, -21, -15, 23, 11, 1, -2, 10, 6, -3, 0, 6, 9, 12, -9, -9, 3, 5, -1, -9, 7, -2, -8, 8, 0, -3, 4, -2, 0, -2, -6, -7, 0, 2, 6, -4, -5, 3, 5, -5, 1, -3, -5, -1, -1, -3, 2, 4, 0, 0, 4, 2, 2, 4, -2, 3],
            pk=[3960926043577541377267819439096804391618603251899293401108425870931130716835203327490747655445345747395550952611253698265199934334080097314192061511711009, 487723576446903129146235324971287599085008588633854300660046476997824105838685867154322957184759627700657558727262867917195404714619211343006256912881095],
            compressed=0x3E533EEDF73446A8FCDAE46A97E4855C62E7A67B6F5CBE2710087C8FFC3C63A4BFA6B365553FB23219955B13A99207721F936E790795CB0AE09D206655197063,
        ),
    )

    ss = 0x13F022ADE3CC59641C069BF9FE798A6AACE097DF451EBDF95DC016978E494748BD9935F311F4D403AFBC96021FD19EBB7381D077D2A1A8D59FC81F97508B4F5A

    prime = 'p512'
    formula = NotImplemented
    style = 'df'
    tuned = False
    exponent = 10
    multievaluation = False
    uninitialized = False
    verbose = False

    def setUp(self):
        self.csidh = CSIDH(
            'montgomery',
            prime=self.prime,
            formula=self.formula,
            style=self.style,
            exponent=self.exponent,
            tuned=self.tuned,
            uninitialized=self.uninitialized,
            multievaluation=self.multievaluation,
            verbose=self.verbose,
        )

        self.coeff = self.csidh.curve.coeff
        self.field = self.csidh.field

    def test_dh_AB(self):
        self.assertEqual(
            self.coeff(self.csidh.gae.GAE_at_A(self.keys.A.sk, [self.field(pk) for pk in self.keys.B.pk])).x,
            self.ss,
        )

    def test_dh_BA(self):
        self.assertEqual(
            self.coeff(self.csidh.gae.GAE_at_A(self.keys.B.sk, [self.field(pk) for pk in self.keys.A.pk])).x,
            self.ss,
        )

    def test_compress(self):
        for keys in self.keys.values():
            self.assertEqual(keys.compressed, self.csidh.curve.coeff([self.field(pk) for pk in keys.pk]).x)

    def test_compress_roundtrip(self):
        compress, uncompress = (
            self.csidh.curve.coeff,
            self.csidh.curve.affine_to_projective,
        )
        for keys in self.keys.values():
            self.assertEqual(
                keys.compressed, compress(uncompress(compress([self.field(pk) for pk in keys.pk]))).x
            )