Beispiel #1
0
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)]
Beispiel #2
0
    def find_binomial_cost(sigma, samples, cost_cl, cost_pq, cost_pp):
        """Estimates the cost of replacing a rounded Gaussian with a binomial.

        Args:
            sigma: The standard deviation of the Gaussian.
            samples: The total number of samples drawn by Alice and Bob.
            cost_cl, cost_pq, cost_pp: Estimated costs of the rounded Gaussian.

        Returns:
            Four-tuple consisting of the distribution and the cost triplet.
        """

        dg = dgauss(sigma)
        # The binomial is defined as B(2*z, .5) - z.
        sb = sym_binomial(2 * sigma**2)

        _, cost_cl_binomial = opt_renyi_bound(-cost_cl * log(2), sb, dg,
                                              samples)
        _, cost_pq_binomial = opt_renyi_bound(-cost_pq * log(2), sb, dg,
                                              samples)
        _, cost_pp_binomial = opt_renyi_bound(-cost_pp * log(2), sb, dg,
                                              samples)

        return [
            sb, -cost_cl_binomial / log(2), -cost_pq_binomial / log(2),
            -cost_pp_binomial / log(2)
        ]
Beispiel #3
0
def distribution_to_TeX(p):
    """Formats distribution for use in TeX file.

    Args:
      p: A dictionary with entries for 'distr', 'sigma', 'a', 'D'

    Returns:
      LaTeX string.
    """

    distr, sigma, a, distr_name = p['distr'], p['sigma'], p['a'], p['D']

    n = max(distr.iterkeys()) + 1  # range is [0..n)
    b = bits_needed_to_sample(distr)
    ret = "${}$ & {} & {:.2f} & ".format(distr_name, b + 1, sigma**2)

    for i in sorted(distr.iterkeys()):
        if i >= 0:
            p = int(round(distr[i] * 2**b))
            if i == 0:
                p *= 2
            ret += r"\!\!{}\!\!&".format(p)

    for i in xrange(max(distr.iterkeys()), 6):
        ret += "&"

    divergence = renyi(distr, nonnegative_half(dgauss(sigma)), a)
    ret += " {:.1f} & {:.7f}".format(a, divergence)
    return ret + r" \\"
Beispiel #4
0
def distribution_to_tex(p):
    """Formats distribution for use in TeX file.

  Args:
    p: A dictionary with entries for 'distr', 'sigma', 'a', 'name'

  Returns:
    LaTeX string.
  """

    distr, sigma, a, distr_name = p['distr'], p['sigma'], p['a'], p['name']

    dg = dgauss(sigma)

    b = bits_needed_to_sample(distr)
    ret = '{} & {:.3f} &'.format(distr_name, sigma)

    for i in sorted(distr.iterkeys()):
        if i >= 0:
            p = int(round(distr[i] * 2**b))
            if i == 0:
                p *= 2
            ret += r'\!\!{}\!\!&'.format(p)

    for i in range(max(distr.iterkeys()), 12):
        ret += '&'

    divergence = renyi(distr, nonnegative_half(dg), a)
    ret += r' {:.1f} & ${:.2f}\times 10^{{-4}}$'.format(a, divergence * 10**4)
    return ret + r' \\'
Beispiel #5
0
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
Beispiel #7
0
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)
Beispiel #8
0
def approximate_dgauss(sigma,
                       samples,
                       base_security,
                       max_table_len,
                       max_rand_bits,
                       suffix='',
                       quiet=True):
    """Approximates rounded Gaussian with a binomial and an optimal discrete distribution.

  Args:
    sigma: The standard deviation of the target Gaussian distribution.
    samples: Total number of samples per protocol run.
    base_security: The baseline security level, in bits (e.g., 150.34).
    max_table_len: Upper bound on the support of the distribution (can be
      None).
    max_rand_bits: Total number of uniformly random bits required for
      sampling.
    suffix: Suffix for printed out names.
    quiet: If quiet, suppress all output.

  Returns:
    (security bound, non-negative part of distribution, optimal Renyi order).
    """

    dg = dgauss(sigma)
    half_dg = nonnegative_half(dg)

    if not quiet:
        print(suffix)
        z = sigma**2 * 2
        if fmod(z, 1.) != 0:
            print('Skipping binomial')
        else:
            sb = sym_binomial(2 * int(z))
            opt_a_sb, opt_bound_sb = opt_renyi_bound(-base_security * log(2),
                                                     sb, dg, samples)

            print(
                'Sigma = {:.3f}: Binomial distribution z = {}, security = {:.2f}, a '
                '= {:.2f};').format(sigma, z, -opt_bound_sb / log(2), opt_a_sb)

    max_security = 0
    opt_r = None
    opt_d = {}
    opt_a = None

    for a in orders:
        for random_bits in range(
                1, max_rand_bits):  # reserve one bit for the sign
            d = round_distr_opt(half_dg,
                                2**-random_bits,
                                a,
                                max_table_len,
                                quiet=True)
            if d is not None:
                r = renyi(d, half_dg, a)
                security = -renyi_bound(-base_security * log(2), r * samples,
                                        a)
                if security > max_security:
                    max_security = security
                    opt_a = a
                    opt_d = d
                    opt_r = r

    if not quiet:
        if max_security == 0:
            print('Approximation is infeasible under given constraints')
        else:
            print('Security = {:.2f} a = {} Renyi divergence = {}'.format(
                max_security / log(2), opt_a, opt_r))
    return [max_security / log(2), opt_d, opt_a]
Beispiel #9
0
def main():
    # pyformat: disable
    parameters = [
        {
            'name': 'Frodo-640',
            'sigma': 2.8,
            'n': 640,
            'm_bar': 8,
            'n_bar': 8,
            'q': 15,
            'B': 2,
            'key_len': 128,
            'rand_bits': 16,
            'sec_base': 105
        },
        {
            'name': 'Frodo-976',
            'sigma': 2.3,
            'n': 976,
            'm_bar': 8,
            'n_bar': 8,
            'q': 16,
            'B': 3,
            'key_len': 192,
            'rand_bits': 16,
            'sec_base': 151
        },
        {
            'name': 'Frodo-1344',
            'sigma': 1.4,
            'n': 1344,
            'm_bar': 8,
            'n_bar': 8,
            'q': 16,
            'B': 4,
            'key_len': 256,
            'rand_bits': 16,
            'sec_base': 195
        },
    ]
    # pyformat: enable

    for p in parameters:
        if p['rand_bits'] is not None:
            samples = (p['n_bar'] +
                       p['m_bar']) * p['n'] + p['n_bar'] * p['m_bar']
            _, p['distr'], p['a'] = approximate_dgauss(p['sigma'],
                                                       samples,
                                                       p['sec_base'],
                                                       None,
                                                       p['rand_bits'],
                                                       quiet=True)
        else:
            gauss_dist = cutoff_tails(dgauss(p['sigma']), 2**-16)
            p['distr'], p['a'] = gauss_dist, float('inf')

    print('### DISTRIBUTION TO PYTHON ###')
    for p in parameters:
        print('sigma = {:.2f}: {}'.format(p['sigma'],
                                          distribution_to_python(p)))
    print()

    print('### C Code ###')
    for p in parameters:
        print(distribution_to_c(p['distr']))
    print()

    print('### TABLE 1 ###')
    for p in parameters:
        print(parameters_to_tex(p))
    print()

    print('### TABLE 2 ###')
    for p in parameters:
        print(distribution_to_tex(p))
    print()

    print('### TABLE 4 ###')
    for p in parameters:
        print(print_sizes(p, 'Frodo', kem=True))
    # print(r'\midrule')
    # for p in parameters:
    #   print(print_sizes(p, 'Frodo', kem=False))
    print()

    print('### PARAMETERS FOR CRYPTANALYIS ###')
    for p in parameters:
        print(security_to_tex(p), end='')
        if p['key_len'] == 256:
            print(r'\bottomrule')
        else:
            print(r'\midrule')
Beispiel #10
0
def approximate_dgauss(sigma,
                       samples,
                       base_security,
                       max_table_len,
                       max_rand_bits,
                       suffix="",
                       quiet=True):
    """Approximates rounded Gaussian with a binomial and an optimal discrete distribution.

      Args:
        sigma: The standard deviation of the target Gaussian distribution.
        samples: Total number of samples per protocol run.
        base_security: The baseline security level, in bits (e.g., 150.34).
        max_table_len: Upper bound on the support of the distribution (can be None).
        max_rand_bits: Total number of uniformly random bits required for
          sampling.
        suffix: Suffix for printed out names.
        quiet: If quiet, suppress all output.

      Returns:
        Optimal rounded distribution (only the non-negative support), its security bound,
        and the order of Renyi divergence used to derive this bound.
    """

    dg = dgauss(sigma)
    half_dg = nonnegative_half(dg)

    if not quiet:
        print suffix
        z = sigma**2 * 2
        if fmod(z, 1.) != 0:
            print "Skipping binomial"
        else:
            sb = sym_binomial(2 * int(z))
            opt_a_sb, opt_bound_sb = opt_renyi_bound(-base_security * log(2),
                                                     sb, dg, samples)

            print(
                "Sigma = {:.3f}: Binomial distribution z = {}, security = {:.2f}, a = "
                "{:.2f};").format(sigma, z, -opt_bound_sb / log(2), opt_a_sb)

    # Constrain Renyi orders of interest to the following set for performance
    # and aesthetic reasons
    a_values = [
        1.5, 2., 5., 10., 15., 20., 25., 30., 40., 50., 75., 100., 200., 500.,
        float("inf")
    ]

    max_security = 0
    opt_r = None
    opt_d = {}
    opt_a = None

    for a in a_values:
        for random_bits in xrange(
                1, max_rand_bits):  # reserve one bit for the sign
            d = round_distr_opt(half_dg,
                                2**-random_bits,
                                a,
                                max_table_len,
                                quiet=True)
            if d is not None:
                r = renyi(d, half_dg, a)
                security = -renyi_bound(-base_security * log(2),
                                        log(r) * samples, a)
                if security > max_security:
                    max_security = security
                    opt_a = a
                    opt_d = d
                    opt_r = r

    if not quiet:
        if max_security == 0:
            print "Approximation is infeasible under given constraints"
        else:
            print "Security = {:.2f} a = {} Renyi divergence = {}".format(
                max_security / log(2), opt_a, opt_r)
    return [max_security / log(2), opt_d, opt_a]