Path: blob/main/castryck_decru_attack.sage
323 views
# Python imports1import time2from itertools import product34# Local Imports5from helpers import possibly_parallel6from richelot_aux import AuxiliaryIsogeny, Does22ChainSplit, Pushing3Chain7from uvtable import uvtable89# Load Sage Files10load('speedup.sage')1112# ===================================13# ===== ATTACK ====================14# ===================================1516def CastryckDecruAttack(E_start, P2, Q2, EB, PB, QB, two_i, num_cores=1):17tim = time.time()1819skB = [] # TERNARY DIGITS IN EXPANSION OF BOB'S SECRET KEY2021# gathering the alpha_i, u, v from table22expdata = [[0, 0, 0] for _ in range(b-3)]23for i in range(b%2, b-3, 2):24index = (b-i) // 225row = uvtable[index-1]26if row[1] <= a:27expdata[i] = row[1:4]2829# gather digits until beta_130bet1 = 031while not expdata[bet1][0]:32bet1 += 133bet1 += 13435ai,u,v = expdata[bet1-1]3637print(f"Determination of first {bet1} ternary digits. We are working with 2^{ai}-torsion.")3839bi = b - bet140alp = a - ai4142@possibly_parallel(num_cores)43def CheckGuess(first_digits):44print(f"Testing digits: {first_digits}")4546scalar = sum(3^k*d for k,d in enumerate(first_digits))47tauhatkernel = 3^bi * (P3 + scalar*Q3)4849tauhatkernel_distort = u*tauhatkernel + v*two_i(tauhatkernel)5051C, P_c, Q_c, _ = AuxiliaryIsogeny(bet1, u, v, E_start, P2, Q2, tauhatkernel, two_i)5253return Does22ChainSplit(C, EB, 2^alp*P_c, 2^alp*Q_c, 2^alp*PB, 2^alp*QB, ai)5455guesses = [ZZ(i).digits(3, padto=bet1) for i in range(3^bet1-1)]5657for result in CheckGuess(guesses):58((first_digits,), _), is_split = result59if is_split is not None:60print("Glue-and-split! These are most likely the secret digits.")61skB += first_digits62break6364else:65print("All other guesses failed, so first digits must be all 2!")66skB += [2]*bet16768print(skB)6970# now compute longest prolongation of Bob's isogeny that may be needed71length = 172max_length = 073for i in range(bet1, b-3):74if expdata[i][0]:75max_length = max(length, max_length)76length = 077else:78length += 17980while True:81K = 2^a*3^(b - max_length)*EB.random_point()82if K.order() == 3^max_length:83break8485while True:86alternativeK = 2^a*3^(b - max_length)*EB.random_point()87if K.weil_pairing(alternativeK, 3^max_length)^(3^(max_length - 1)) != 1:88break8990_, EBprolong = Pushing3Chain(EB, K, max_length)9192# gather next digit and change prolongation if needed93i = bet1 + 194bi = b - i9596print(f"Determination of the {i}th ternary digit. We are working with 2^{ai}-torsion.")97prolong = 198print("Prolonging with 1 steps.")99endPB = EBprolong[0](PB)100endQB = EBprolong[0](QB)101endEB = EBprolong[0].codomain()102# Speeds things up in Sage103endEB.set_order((p+1)^2, num_checks=0)104105positives = []106107@possibly_parallel(num_cores)108def CheckGuess(j):109print(f"Testing digit: {j}")110111scalar = sum(3^k*d for k,d in enumerate(skB + [j]))112tauhatkernel = 3^bi * (P3 + scalar*Q3)113114C, P_c, Q_c, _ = AuxiliaryIsogeny(i, u, v, E_start, P2, Q2, tauhatkernel, two_i)115116return Does22ChainSplit(C, endEB, 2^alp*P_c, 2^alp*Q_c, 2^alp*endPB, 2^alp*endQB, ai)117118for result in CheckGuess([0,1,2]):119((j,), _), is_split = result120if is_split:121print("Glue-and-split!")122positives.append(j)123# continue testing other digits unless we reached an124# ambiguity already:125# By Remark 4 of [Castryck-Decru], there a probability126# that K cancels the tail of Bob's isogeny, creating false127# positives, in this case, we have to switch to alternativeK.128if len(positives) > 1:129break130131if len(positives) == 1:132print(f"Most likely good prolongation and the secret digit is: {positives[0]}")133skB.append(positives[0])134print(skB)135next_i = i + 1136else:137print("All glue-and-splits, must be bad prolongation: changing it and redoing this digit.")138_, EBprolong = Pushing3Chain(EB, alternativeK, max_length)139next_i = i140prolong = 0141142143# now gather all remaining digits, except for last three (we close that gap by trial and error)144145for i in range(next_i, b-2):146bi = b - i147if expdata[i-1][0]:148ai,u,v = expdata[i-1]149alp = a - ai150prolong = 0151else:152prolong += 1153154print(f"Determination of the {i}th ternary digit. We are working with 2^{ai}-torsion.")155print(f"Prolonging with {prolong} steps.")156endPB = PB157endQB = QB158endEB = EB159# for j in [1..prolong] do160for j in range(1,prolong+1):161endPB = EBprolong[j-1](endPB)162endQB = EBprolong[j-1](endQB)163if j == prolong:164endEB = EBprolong[j-1].codomain()165# Speeds things up in Sage166endEB.set_order((p+1)^2, num_checks=0)167168@possibly_parallel(num_cores)169def CheckGuess(j):170print(f"Testing digit: {j}")171172scalar = sum(3^k*d for k,d in enumerate(skB + [j]))173tauhatkernel = 3^bi * (P3 + scalar * Q3)174175C, P_c, Q_c, _ = AuxiliaryIsogeny(i, u, v, E_start, P2, Q2, tauhatkernel, two_i)176177return Does22ChainSplit(C, endEB, 2^alp*P_c, 2^alp*Q_c, 2^alp*endPB, 2^alp*endQB, ai)178179for result in CheckGuess([0,1]):180((j,), _), is_split = result181if is_split:182print("Glue-and-split! This is most likely the secret digit.")183skB.append(j)184print(skB)185break186187else:188print("All other guesses failed, so the digit must be 2")189skB.append(2)190print(skB)191192key = sum([skB[i]*3^(i) for i in range(b-3)])193194# bridge last safety gap195tim2 = time.time()196197@possibly_parallel(num_cores)198def CheckGuess(i):199bobskey = key + i*3^(b-3)200bobscurve, _ = Pushing3Chain(E_start, P3 + bobskey*Q3, b)201return bobscurve.j_invariant() == EB.j_invariant()202203print(f"Determination of last {3} ternary digits. We are brute-forcing this.")204205for result in CheckGuess([0..3^3]):206((i,), _), found = result207if found:208bobskey = key + i*3^(b-3)209break210211print(f"Bridging last gap took: {time.time() - tim2}")212213if found:214print(f"Bob's secret key revealed as: {bobskey}")215print(f"In ternary, this is: {Integer(bobskey).digits(base=3)}")216print(f"Altogether this took {time.time() - tim} seconds.")217return bobskey218else:219print("Something went wrong.")220print(f"Altogether this took {time.time() - tim} seconds.")221return None222223224