|
| 1 | +# bootstrapping-lwe-estimator.sage |
| 2 | +# |
| 3 | +# A script that uses the lwe-estimator to estimate the security levels |
| 4 | +# for various bootstrapping parameters. This file prints the results |
| 5 | +# to stdout in CSV format, where the columns are: |
| 6 | +# m, n, |q|, X=n/log2(1/alpha), Y=security |
| 7 | +# |
| 8 | +# The script also print *to stderr* the linear regression line for |
| 9 | +# all the X,Y values. |
| 10 | +# |
| 11 | +# The bootsrapping parameters are specified by the ms, ns, and qs |
| 12 | +# lists below, where n[i]=phi(m[i]) and q[i] is the bit-length of |
| 13 | +# the modulus in the key-switching matrices, namely |
| 14 | +# |q| = log2(ctxtPrimes)+log2(specialPrimes). |
| 15 | + |
| 16 | +import sys |
| 17 | +import itertools as it |
| 18 | + |
| 19 | +print("Usage: sage", sys.argv[0], "[HammingWeight]\n") |
| 20 | +print(" if HammingWeight is not supplied, will use [120,150,180,...,480]") |
| 21 | +print(" and dense keys. If HammingWeight='dense' only dense keys are used.\n") |
| 22 | +print(" Data is saved in lwe-estimate-<weight>.csv files, and plots") |
| 23 | +print(" of the data are saved in lwe-estplot-<weight>.pdf files.") |
| 24 | + |
| 25 | +if len(sys.argv) > 1: |
| 26 | + if sys.argv[1].isnumeric() and int(sys.argv[1])>0: |
| 27 | + weights = [int(sys.argv[1])] |
| 28 | + else: |
| 29 | + weights = ['dense'] |
| 30 | +else: |
| 31 | + weights=[120] |
| 32 | +# weights=range(120,500,30) |
| 33 | + |
| 34 | +# We have the two estimates sec = 3.8*x -15 and sec=2.53*x +19, |
| 35 | +# for dense and sparse(120), respectively, where |
| 36 | +# x = n/log_2(1/alpha) = n/log_2(sigma*sqrt(2 pi)/q). |
| 37 | +# n/x = log_2(1/alpha) = log_2(q) -log_2(sigma*sqrt(2 pi)) |
| 38 | +# q = 2^{n/x} * sigma*sqrt(2 pi) |
| 39 | +# |
| 40 | +# To set the range of |q| values on which to run the estimator, we aim |
| 41 | +# at security roughly between 80 and 256, so we roughly parameters that |
| 42 | +# give x values between 20 and 80. |
| 43 | + |
| 44 | +# Load the estimator (change the location if it is stored elesewhere) |
| 45 | +# load("https://bitbucket.org/malb/lwe-estimator/raw/HEAD/estimator.py") |
| 46 | +load("~/opt/lwe-estimator/estimator.py") |
| 47 | + |
| 48 | +# Disable information printouts from the estimator |
| 49 | +logging.getLogger("estimator").setLevel(logging.WARNING) |
| 50 | + |
| 51 | +# Remove all the tests except "usvp" and "dual" |
| 52 | +toskip=("mitm", "arora-gb", "bkw", "dec") |
| 53 | + |
| 54 | +def estimate_hw(hw): |
| 55 | + data = [] |
| 56 | + minX = 100000 |
| 57 | + maxX = 0 |
| 58 | + csvfile = open("lwe-estimate-"+str(hw)+".csv", "w") |
| 59 | + print("n,|q|,log2(1/alpha),x=n/log2(1/alpha),security ("+str(hw)+")", file=csvfile) |
| 60 | + for n in range(2049, 30000, 2048): |
| 61 | + # some data points with sigma*sqrt(2\pi)=8, others with 8*sqrt(n) |
| 62 | + fromLogq = int(round(n/80, 0)) |
| 63 | + toLogq = int(round(n/20, 0)) |
| 64 | + # Use more q's at the lower range (=higher-security) |
| 65 | + qs = it.chain(range(fromLogq+5,fromLogq+50,10),range(fromLogq,toLogq,50)) |
| 66 | + for logQ in qs: |
| 67 | + q = sage.rings.all.RR(2^logQ) +1 |
| 68 | + for s2p in [8.0, 8.0*math.sqrt(n)]: |
| 69 | + alpha = s2p/sage.rings.all.RR(q) |
| 70 | + |
| 71 | + # run the estimator to get the complexity of "usvp" and "dual" attacks |
| 72 | + if isinstance(hw, int): |
| 73 | + dist=((-1,1),hw) # sparse weight-hw |
| 74 | + else: |
| 75 | + dist=(-1,1) # dense in {-1,0,1} |
| 76 | + |
| 77 | + est = estimate_lwe(n, alpha, q, reduction_cost_model=BKZ.sieve,secret_distribution=dist,skip=toskip) |
| 78 | + |
| 79 | + # FIXME: can we always assume that "usvp" and "dual" will be there? |
| 80 | + sec = min(math.log2(est["usvp"]["rop"]), math.log2(est["dual"]["rop"])) |
| 81 | + if sec < 70 or sec > 270: |
| 82 | + continue; |
| 83 | + |
| 84 | + # record n/log2(1/alpha) and the security |
| 85 | + loga = -log(alpha)/log(2.0) |
| 86 | + xx = n / loga |
| 87 | + if xx < minX: |
| 88 | + minX = xx |
| 89 | + if xx > maxX: |
| 90 | + maxX = xx |
| 91 | + data.append([xx,sec]) |
| 92 | + |
| 93 | + # print one line of the CSV file |
| 94 | + print(str(n)+','+str(logQ)+','+str(round(loga,1))+','+str(round(xx,1))+','+str(round(sec,1)), file=csvfile) |
| 95 | + |
| 96 | + csvfile.close() |
| 97 | + |
| 98 | + # Fit the data points to an affine line |
| 99 | + var('a,b') |
| 100 | + model(x)=a*x+b |
| 101 | + line=find_fit(data,model,solution_dict=True) |
| 102 | + |
| 103 | + # Plot it so you can see that it makes sense |
| 104 | + title = str(hw)+": sec = "+str(round(line[a],1))+"X + "+str(round(line[b],1)) |
| 105 | + p=points(data)+plot(model(a=line[a],b=line[b]),(x,minX,maxX),color='black',title=title) |
| 106 | + p.save('lwe-estplot-'+str(hw)+'.pdf') |
| 107 | + |
| 108 | + # Also print it to the terminal |
| 109 | + print(title) |
| 110 | + |
| 111 | + |
| 112 | +for hw in weights: |
| 113 | + estimate_hw(hw) |
| 114 | + |
| 115 | +if len(sys.argv) < 2: # no HW argument, estimate dense as well |
| 116 | + estimate_hw('dense') |
0 commit comments