def noise_failure_prob(noise, q, n, w, reclen, g, cut): m = 2**w d = max(filter(lambda x: m*x*2 < q*(1. - 1./g), range(q))) print "d = ", d eps = sym_uniform(2**cut) noise_sqr = pdf_product(noise, noise, q) # noise_sqr = noise * noise noise_eps = pdf_product(noise, convolution(noise, eps, q), q) # noise_eps = noise * (noise + eps) v1 = nfoldconvolution(n, noise_sqr, q) # v1 = n * (noise * noise) v2 = nfoldconvolution(n, noise_eps, q) # v2 = n * (noise * (noise + eps)) vv = convolution(convolution(v1, v2, q), noise, q) # vv = v1 + v2 + noise failure_pr = reclen * sum(map(lambda x: vv[x], filter(lambda x: min(x, q - x) > d, vv.keys()))) return failure_pr
def find_opt_distr(sigma, samples, ubits, cost_cl, cost_pq, cost_pp): """Finds an optimal distribution approximating rounded continuous Gaussian. Args: sigma: The standard deviation of the target (rounded) Gaussian. samples: The total number of samples drawn by both parties combined. ubits: The bound on the number of uniform bits required for sampling. cost_cl, cost_pq, cost_pp: Estimated costs of the rounded Gaussian. Returns: Four-tuple consisting of the distribution and the cost triplet. """ cost_cl_opt, d, _ = approximate_dgauss(sigma, samples, cost_cl, None, ubits, quiet=True) sym_d = pdf_product(d, {+1: .5, -1: .5}) dg = dgauss(sigma) _, cost_pq_opt = opt_renyi_bound(-cost_pq * log(2), sym_d, dg, samples) _, cost_pp_opt = opt_renyi_bound(-cost_pp * log(2), sym_d, dg, samples) return [sym_d, cost_cl_opt, -cost_pq_opt / log(2), -cost_pp_opt / log(2)]
def parameters_to_TeX(p, nbar): """Formats parameters for use in TeX file. Args: p: A dictionary with entries for 'name', 'n', 'q', 'D', 'B', 'distr' nbar: Number of columns in exchanged matrices. Returns: LaTeX string. """ name, n, q, dname, B, distr = p['name'], p['n'], p['q'], p['D'], p['B'], p[ 'distr'] s = name.capitalize() s += " & \!\! {} \!\!".format(n) s += " & \!\! $2^{{{}}}$ \!\!".format(q) s += " & \!\! ${}$ \!\!".format(dname) agreed_bits = B * nbar * nbar s += " & ${}\cdot {}^2 = {}$".format(B, nbar, agreed_bits) if agreed_bits < 100: # pad to equal length s += "\hphantom{0}" sym_distr = pdf_product(distr, {+1: .5, -1: .5}) failure_prob = noise_failure_prob(sym_distr, 2**q, n, B, agreed_bits) s += " & $2^{{{:.1f}}}$".format(log(failure_prob, 2)) bandwidth = nbar * q * 2 * n + nbar * nbar # bandwidth in bits s += " & {:.2f} KB".format(bandwidth / 8000.) s += r" \\" return s
def parameters_to_TeX(p, nbar): """Formats parameters for use in TeX file. Args: p: A dictionary with entries for 'name', 'n', 'q', 'D', 'B', 'distr' nbar: Number of columns in exchanged matrices. Returns: LaTeX string. """ name, n, q, dname, B, distr, g = p['name'], p['n'], p['q'], p['D'], p[ 'B'], p['distr'], p['g'] s = name.capitalize() s += " n = {}".format(n) s += " q = $2^{{{}}}$".format(q) s += " distr = ${}$".format(dname) agreed_bits = B * nbar * nbar s += " ${}\cdot {}^2 = {}$".format(B, nbar, agreed_bits) sym_distr = pdf_product(distr, {+1: .5, -1: .5}) failure_prob = noise_failure_prob(sym_distr, 2**q, n, B, agreed_bits, g, p['cut']) s += " err = $2^{{{:.1f}}}$".format(log(failure_prob, 2)) bandwidth = nbar * q * 2 * n + nbar * nbar * ceil(log( g, 2)) # bandwidth in bits s += " & {:.2f} kB".format(bandwidth / 8000.) s += r" \\" return s
def parameters_to_tex(p): """Formats parameters for use in TeX file. Args: p: A dictionary with entries for 'name', 'n', 'q', 'D', 'B', 'distr' Returns: LaTeX string. """ def print_cost(attack_type): _, _, cost_pc = optimize_attack( 2**q, n, max(nbar, mbar) + n, sigma, dual_cost, attack_type ) # Only compute the dual cost, since it is smaller than the primal cost. cost_pc -= log(nbar + mbar) / log( 2 ) # Take into account the hybrid argument over mbar + nbar instances. _, cost_pc_reduced = opt_renyi_bound(-cost_pc * log(2), sym_distr, dg, samples) return ' & {}'.format(int(-cost_pc_reduced / log(2))) name, n, q, b, distr, sigma, nbar, mbar, keylen = (p['name'], p['n'], p['q'], p['B'], p['distr'], p['sigma'], p['n_bar'], p['m_bar'], p['key_len']) s = name.capitalize() s += r' & \!\! {} \!\!'.format(n) s += r' & \!\! $2^{{{}}}$ \!\!'.format(q) sigma_str = r'{:.3f}'.format(sigma).rstrip('0.') s += r' &\quad ' + sigma_str s += r' &$[{}\dots {}]$'.format(-max(distr), max(distr)) s += r' &\!\!{}\!\!'.format(b) s += r' & ${}\times {}$'.format(nbar, mbar) sym_distr = pdf_product(distr, {+1: .5, -1: .5}) failure_prob = exact_failure_prob_pke(sym_distr, 2**q, n, b, keylen) if failure_prob == 0: s += r' & $0$ ' else: s += r' & $2^{{{:.1f}}}$'.format(log(failure_prob) / log(2)) ct_len = ((mbar * n + mbar * nbar) * q + keylen) // 8 s += r' & {}'.format(ct_len) samples = n * (nbar + mbar) + nbar * mbar dg = dgauss(sigma) s += print_cost(svp_classical) # s += print_cost(svp_quantum) s += print_cost(svp_plausible) s += r' \\' return s
def security_to_TeX(p, nbar, print_sec=True): """Formats security estimates for use in TeX file. Args: p: A dictionary with entries for 'name', 'n', 'q', 'distr', 'sigma' nbar: Number of columns in exchanged matrices. print_sec: If true, output will include security estimates Returns: LaTeX string. """ name, n, qlog, d, sigma = p['name'], p['n'], p['q'], p['distr'], p['sigma'] samples = 2 * n * nbar + nbar**2 q = 2**qlog ret = "" ret += r"\multirow{2}{*}{" + name.capitalize() + "} " for cost in [primal_cost, dual_cost]: m_pc, b_pc, cost_pc = optimize_attack(q, n, samples, sigma, cost, svp_classical, verbose=False) m_pq, b_pq, cost_pq = optimize_attack(q, n, samples, sigma, cost, svp_quantum, verbose=False) m_pp, b_pp, cost_pp = optimize_attack(q, n, samples, sigma, cost, svp_plausible, verbose=False) if cost == primal_cost: ret += "& Primal & " else: ret += "& Dual & " ret += "{} & {} &".format(m_pc, b_pc) if print_sec: sym_d = pdf_product(d, {+1: .5, -1: .5}) dg = dgauss(sigma) _, cost_pc_reduced = opt_renyi_bound(-cost_pc * log(2), sym_d, dg, samples) _, cost_pq_reduced = opt_renyi_bound(-cost_pq * log(2), sym_d, dg, samples) _, cost_pp_reduced = opt_renyi_bound(-cost_pp * log(2), sym_d, dg, samples) ret += "{} & {} & {} & {} & {} & {} \\\\".format( int(cost_pc), int(cost_pq), int(cost_pp), int(-cost_pc_reduced / log(2)), int(-cost_pq_reduced / log(2)), int(-cost_pp_reduced / log(2))) # always round down else: ret += "-- & -- & -- & -- & -- & -- \\\\" ret += "\n" return ret
def main(): d1 = {0: 44 / 128., 1: 61 / 128., 2: 20 / 128., 3: 3. / 128} sym_d1 = pdf_product(d1, {+1: .5, -1: .5}) print "Parameters leading to deliberately large probability of failure (for testing purposes):" print "Distribution = D1,", print_failure_props(sym_d1, 10, 320, 1, 64) print "Recommended parameters:" d3 = { 0: 603 / 2048., 1: 919 / 2048., 2: 406 / 2048., 3: 104 / 2048., 4: 15 / 2048., 5: 1 / 2048. } sym_d3 = pdf_product(d3, {+1: .5, -1: .5}) print "Distribution = D3,", print_failure_props(sym_d3, 15, 752, 4, 256) print "Parameters similar to recommended, distribution is different:" dg175 = dgauss(sqrt(1.75)) print "Distribution = rounded Gaussian with sigma^2 = 1.75,", print_failure_props(dg175, 15, 752, 4, 256)
def main(): print "RLWE with OKCN, AKCN" bandwith = lambda q, n, g : (ceil(log(q, 2)) * n * 2 + ceil(log(g, 2)) * n + 256)*1. / 8 q, n, m = 12289, 1024, 2 reclen = n; noise = sym_binomial(32) noise_sqr = pdf_product(noise, noise, q) v = nfoldconvolution(2*n, noise_sqr, q) v = convolution(v, noise, q) # v = 2*n * (noise * noise) + noise print getVariance(v, q), 2.*n*getVariance(noise, q)**2 + getVariance(noise, q) g = 2**2 d = okcn_get_d(q, n, m, g) failure_pr = sum(map(lambda x: v[x], filter(lambda x: min(x, q - x) > d, v.keys()))) print "OKCN q = {}, n = {}, m = {}, g = {}, d = {}, bandwith = {}".format(q, n, m, g, d, bandwith(q, n, g)) print "per bit pr of failure = 2^{:.2f}".format(log(failure_pr, 2)) print g = 2**3 d = okcn_get_d(q, n, m, g) failure_pr = sum(map(lambda x: v[x], filter(lambda x: min(x, q - x) > d, v.keys()))) print "OKCN q = {}, n = {}, m = {}, g = {}, d = {}, bandwith = {}".format(q, n, m, g, d, bandwith(q, n, g)) print "per bit pr of failure = 2^{:.2f}".format(log(failure_pr, 2)) print g = 2**4 d = okcn_get_d(q, n, m, g) failure_pr = sum(map(lambda x: v[x], filter(lambda x: min(x, q - x) > d, v.keys()))) print "OKCN q = {}, n = {}, m = {}, g = {}, d = {}, bandwith = {}".format(q, n, m, g, d, bandwith(q, n, g)) print "per bit pr of failure = 2^{:.2f}".format(log(failure_pr, 2)) print g = 2**4 d = akcn_get_d(q, n, m, g) failure_pr = sum(map(lambda x: v[x], filter(lambda x: min(x, q - x) > d, v.keys()))) print "AKCN q = {}, n = {}, m = {}, g = {}, d = {}, bandwith = {}".format(q, n, m, g, d, bandwith(q, n, g)) print "per bit pr of failure = 2^{:.2f}".format(log(failure_pr, 2)) print g = 2**6 d = akcn_get_d(q, n, m, g) failure_pr = sum(map(lambda x: v[x], filter(lambda x: min(x, q - x) > d, v.keys()))) print "AKCN q = {}, n = {}, m = {}, g = {}, d = {}, bandwith = {}".format(q, n, m, g, d, bandwith(q, n, g)) print "per bit pr of failure = 2^{:.2f}".format(log(failure_pr, 2)) print
def exact_failure_prob_pke(noise, q, n, w, reclen): """Computes the probability of failure of PKE (exactly). Args: noise: The noise distribution as a dictionary. q: Modulus. n: Vector length. w: Number of extracted bits per coordinate. reclen: Number of bits to be extracted. Returns: The probability of failure. """ def pr_rec_failure_pke(x): # Probability of failing to recover from an error of magnitude x. # 0% if -B/2 <= x < B/2 # 100% if x >= B/2 or x < -B/2 x = min(x, q - x) b = q / (2**w) if x >= b / 2: return 1. elif x < -b / 2: return 1. else: return 0 noise_sqr = pdf_product(noise, noise, q) v = nfoldconvolution(2 * n, noise_sqr, q) v = convolution(v, noise, q) # v = 2n * (noise^2) + noise exact_pr = {x: p * pr_rec_failure_pke(x) for (x, p) in v.iteritems()} r1r2 = (reclen + w - 1) / w failure_pr = r1r2 * sum(exact_pr.itervalues()) return failure_pr
def noise_failure_prob(noise, q, n, w, reclen): """Computes the failure probability of key agreement for given noise distribution. Args: noise: Noise distribution given as a dictionary. q: Modulus. n: Vector length. w: Number of extracted bits per coordinate. reclen: Number of coordinates to be reconciled. Returns: The probability of failure. """ def pr_rec_failure(x): """ Probability of failing to recover from an error of magnitude x: 0% if x <= B/4 100% if x >= 3B/4 and linear in-between """ x = min(x, q - x) B = q / (2**w) if x < B / 4: return 0 elif x > 3 * B / 4: return 1. else: return (x - B / 4.) / (2. * B / 4.) noise_sqr = pdf_product(noise, noise, q) v = nfoldconvolution(2 * n, noise_sqr, q) v = convolution(v, noise, q) # v = 2n * (noise^2) + noise exact_pr = {x: p * pr_rec_failure(x) for (x, p) in v.iteritems()} failure_pr = reclen * sum(exact_pr.itervalues()) return failure_pr
def approximate_and_compute_failure_pr(qlog, n, sigma, m_bar, n_bar, agree_bits, w): samples = 2 * (m_bar + n_bar) * n + m_bar * n_bar _, d, a = approximate_dgauss(sigma, samples, 149, None, 16, quiet=True) sym_distr = pdf_product(d, {+1: .5, -1: .5}) return exact_failure_prob_pke(sym_distr, 2 ** qlog, n, w, agree_bits)