Checkpoint the party/runtime stack before share-program and malicious-mode work.
Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
695f8e84f7
commit
0d22946a0e
1835 changed files with 170291 additions and 2849 deletions
|
|
@ -11,9 +11,10 @@
|
|||
/// cancels.
|
||||
///
|
||||
/// Default calls take XOR shares of the point. Tagged with
|
||||
/// `arith_input`, the point is the ring sum of the two shares; path
|
||||
/// bits are opened by a carry chain inside the local CW protocol so
|
||||
/// the words match `make_dpf(x0 + x1)` at the caller's query.
|
||||
/// `arith_input`, the point is the ring sum of the two shares. A
|
||||
/// beaver ripple-carry converts those shares to XOR shares of the
|
||||
/// sum bits before the walk, so the words match `make_dpf` on that
|
||||
/// sum. The sum is not opened.
|
||||
///
|
||||
/// `geneval_cmp` is the comparison-channel form. The value-correction
|
||||
/// word is a function of the secret path at every level, so the walk
|
||||
|
|
@ -21,6 +22,7 @@
|
|||
/// Doerner–Shelat comparison key. Prefix shares are
|
||||
/// `eval_point(cmp, ...)` at each endpoint. Piecewise-cubic evaluation
|
||||
/// on top of that is `grotto::geneval_offset_horner`.
|
||||
/// @note The per-level correction opening follows Jack Doerner and abhi shelat, CCS 2017 (ePrint 2017/827). They return a reusable key. This function opens a word only for nodes on the public query trie and, with a local pad tape, sends nothing.
|
||||
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
||||
/// see [LICENSE.md](@ref license) for details.
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
#include "dpf/doerner_shelat.hpp"
|
||||
#include "dpf/eval_target.hpp"
|
||||
#include "dpf/leaf_node.hpp"
|
||||
#include "dpf/verifiable.hpp"
|
||||
|
||||
namespace dpf
|
||||
{
|
||||
|
|
@ -69,6 +72,9 @@ struct geneval_result
|
|||
std::size_t live_levels = 0;
|
||||
bool leaf_live = false;
|
||||
Leaf leaf{};
|
||||
/// @brief Party 0 / 1 VDPF tokens over the live eval trie (empty when unused).
|
||||
proof_token proof0{};
|
||||
proof_token proof1{};
|
||||
};
|
||||
|
||||
namespace detail
|
||||
|
|
@ -172,7 +178,8 @@ auto geneval_run(bool arith, bool arith_out, InputT x0, InputT x1,
|
|||
InputT x0c = x0;
|
||||
InputT x1c = x1;
|
||||
proto.encode_walk_shares(x0c, x1c, arith);
|
||||
const InputT alpha = utils::xor_input_shares(x0c, x1c);
|
||||
// Keep the secret path on share-bits. Do not form a clear alpha for leaf
|
||||
// placement, live levels, or correction seeds.
|
||||
|
||||
std::vector<InputT> flipped;
|
||||
flipped.reserve(queries.size());
|
||||
|
|
@ -191,8 +198,6 @@ auto geneval_run(bool arith, bool arith_out, InputT x0, InputT x1,
|
|||
if (unique_leaves.size() > (std::size_t{1} << 20))
|
||||
throw std::length_error("geneval trie is too large");
|
||||
|
||||
const uint64_t secret_leaf = geneval_leaf_id<dpf_type>(alpha);
|
||||
|
||||
constexpr auto to_int = utils::to_integral_type<InputT>{};
|
||||
|
||||
using tree = dpf::tree_traits<InteriorPRG>;
|
||||
|
|
@ -222,14 +227,17 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
|||
std::memset(&result.leaf, 0, sizeof(result.leaf));
|
||||
result.correction_words.reserve(depth);
|
||||
result.correction_advice.reserve(depth);
|
||||
result.proof0 = detail::vdpf::zero_proof();
|
||||
result.proof1 = detail::vdpf::zero_proof();
|
||||
|
||||
auto mask = dpf_type::msb_mask;
|
||||
bool still_live = true;
|
||||
uint64_t secret_prefix = 0;
|
||||
for (std::size_t level = 0; level < depth; ++level, mask >>= 1)
|
||||
{
|
||||
const uint8_t bit0 = static_cast<uint8_t>(!!(to_int(mask) & to_int(x0c)));
|
||||
const uint8_t bit1 = static_cast<uint8_t>(!!(to_int(mask) & to_int(x1c)));
|
||||
const uint64_t parent_id = geneval_prefix(secret_leaf, depth, level);
|
||||
const uint64_t parent_id = secret_prefix;
|
||||
const bool is_last = tree::is_last_level(level, depth);
|
||||
|
||||
node L0 = simde_mm_setzero_si128();
|
||||
|
|
@ -303,6 +311,8 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
|||
const node cw0 = tree::pack_cw(cw, advice, false, is_last);
|
||||
const node cw1 = tree::pack_cw(cw, advice, true, is_last);
|
||||
const std::size_t child_bits = level + 1;
|
||||
const uint8_t secret_bit = static_cast<uint8_t>((bit0 ^ bit1) & 1u);
|
||||
secret_prefix = (secret_prefix << 1) | secret_bit;
|
||||
std::vector<slot> next;
|
||||
next.reserve(exps.size() * 2);
|
||||
for (const exp & e : exps)
|
||||
|
|
@ -322,9 +332,37 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
|||
dpf::xor_if_lo_bit(e.R1, cw1, e.s1)});
|
||||
}
|
||||
}
|
||||
|
||||
// Fold every live child into both parties' VDPF tokens.
|
||||
if (!next.empty())
|
||||
{
|
||||
cs_block cs{};
|
||||
bool have_cs = false;
|
||||
for (const slot & c : next)
|
||||
{
|
||||
if (c.id == secret_prefix)
|
||||
{
|
||||
// Prefix is the share-bit path accumulated above — not a
|
||||
// fresh xor_input_shares of the point for leaf placement.
|
||||
cs = detail::vdpf::make_cs(level, c.id, c.s0, c.s1);
|
||||
have_cs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!have_cs)
|
||||
cs = detail::vdpf::make_cs(level, next[0].id, next[0].s0,
|
||||
next[0].s1);
|
||||
for (const slot & c : next)
|
||||
{
|
||||
detail::vdpf::fold_node(result.proof0, level, c.id, c.s0, cs);
|
||||
detail::vdpf::fold_node(result.proof1, level, c.id, c.s1, cs);
|
||||
}
|
||||
}
|
||||
|
||||
frontier = std::move(next);
|
||||
}
|
||||
|
||||
const uint64_t secret_leaf = secret_prefix;
|
||||
result.leaf_live = geneval_any_prefix(unique_leaves, depth, secret_leaf, depth);
|
||||
if (result.leaf_live)
|
||||
{
|
||||
|
|
@ -343,18 +381,21 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
|||
{
|
||||
const uint8_t t0 = static_cast<uint8_t>(dpf::get_lo_bit(on->s0));
|
||||
const uint8_t t1 = static_cast<uint8_t>(dpf::get_lo_bit(on->s1));
|
||||
const std::size_t lane = static_cast<std::size_t>(to_int(alpha));
|
||||
result.leaf = proto.template open_arith_leaf<ExteriorPRG, 0, outputs_tuple>(
|
||||
dpf::unset_lo_2bits(on->s0), dpf::unset_lo_2bits(on->s1), t0, t1,
|
||||
y0, y1, std::size_t{0}, lane);
|
||||
y0, y1, std::size_t{0}, x0c, x1c);
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool sign0 = dpf::get_lo_bit(on->s0);
|
||||
auto built = dpf::make_leaves<ExteriorPRG>(alpha,
|
||||
dpf::unset_lo_2bits(on->s0), dpf::unset_lo_2bits(on->s1), sign0,
|
||||
std::size_t{0}, y0);
|
||||
result.leaf = std::get<0>(built.first.first);
|
||||
// Mux / reconstruct only inside the leaf protocol hook.
|
||||
proto.open_leaf_group(x0c, x1c, [&](InputT sx0, InputT sx1) {
|
||||
const InputT x = utils::xor_input_shares(sx0, sx1);
|
||||
const bool sign0 = dpf::get_lo_bit(on->s0);
|
||||
auto built = dpf::make_leaves<ExteriorPRG>(x,
|
||||
dpf::unset_lo_2bits(on->s0), dpf::unset_lo_2bits(on->s1),
|
||||
sign0, std::size_t{0}, y0);
|
||||
result.leaf = std::get<0>(built.first.first);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -491,6 +532,10 @@ std::vector<InputT> geneval_inclusive(InputT from, InputT to)
|
|||
/// @param rng the Doerner–Shelat randomness tapes
|
||||
/// @param y the payload
|
||||
/// @return the opened shares and correction words
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -512,6 +557,10 @@ auto geneval_point(InputT x0, InputT x1, InputT query,
|
|||
/// @param rng the Doerner–Shelat randomness tapes
|
||||
/// @param y the payload
|
||||
/// @return the opened shares and correction words
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -534,6 +583,10 @@ auto geneval_point(arith_input_t, InputT x0, InputT x1, InputT query,
|
|||
/// @param y0 party 0's share of the payload
|
||||
/// @param y1 party 1's share of the payload
|
||||
/// @return the opened shares and correction words
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -556,6 +609,10 @@ auto geneval_point(arith_output_t, InputT x0, InputT x1, InputT query,
|
|||
/// @param y0 party 0's share of the payload
|
||||
/// @param y1 party 1's share of the payload
|
||||
/// @return the opened shares and correction words
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -586,6 +643,10 @@ auto geneval_point(arith_input_t, arith_output_t, InputT x0, InputT x1,
|
|||
/// @param rng the Doerner–Shelat randomness tapes
|
||||
/// @param y the `y`
|
||||
/// @return Geneval on the inclusive interval `[from, to]`
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -600,6 +661,10 @@ auto geneval_interval(InputT x0, InputT x1, InputT from, InputT to,
|
|||
detail::geneval_inclusive(from, to), rng.root, rng.pad, y);
|
||||
}
|
||||
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -614,7 +679,10 @@ auto geneval_interval(arith_input_t, InputT x0, InputT x1, InputT from,
|
|||
detail::geneval_inclusive(from, to), rng.root, rng.pad, y);
|
||||
}
|
||||
|
||||
/// @brief Geneval on the whole domain. Refuses a domain above 2^20 inputs.
|
||||
/// @brief Geneval on the whole domain.
|
||||
/// @details Materializes the query list. Domains wider than 20 bits refuse so
|
||||
/// a caller does not allocate a `2^n` vector by accident. Prefer the
|
||||
/// buffer overloads when writing into a pre-sized output scratch.
|
||||
/// @tparam InteriorPRG PRG that expands interior nodes. Defaults to `dpf::prg::aes128`
|
||||
/// @tparam ExteriorPRG PRG that expands the root. Defaults to `InteriorPRG`
|
||||
/// @tparam InputT input domain type
|
||||
|
|
@ -626,6 +694,10 @@ auto geneval_interval(arith_input_t, InputT x0, InputT x1, InputT from,
|
|||
/// @param rng the Doerner–Shelat randomness tapes
|
||||
/// @param y the `y`
|
||||
/// @return Geneval on the whole domain
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -640,6 +712,10 @@ auto geneval_full(InputT x0, InputT x1,
|
|||
detail::geneval_full_domain<InputT>(), rng.root, rng.pad, y);
|
||||
}
|
||||
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -669,6 +745,10 @@ auto geneval_full(arith_input_t, InputT x0, InputT x1,
|
|||
/// @param rng the Doerner–Shelat randomness tapes
|
||||
/// @param y the `y`
|
||||
/// @return Geneval on a public sequence, in the order given
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -685,6 +765,10 @@ auto geneval_sequence(InputT x0, InputT x1, ForwardIterator begin,
|
|||
std::move(qs), rng.root, rng.pad, y);
|
||||
}
|
||||
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -721,6 +805,8 @@ struct geneval_cmp_result
|
|||
uint64_t addend1 = 0;
|
||||
uint64_t mask = 0;
|
||||
std::size_t live_levels = 0;
|
||||
proof_token proof0{};
|
||||
proof_token proof1{};
|
||||
};
|
||||
|
||||
/// @name Comparison geneval
|
||||
|
|
@ -762,7 +848,7 @@ geneval_cmp_result geneval_cmp(InputT x0, InputT x1,
|
|||
return out;
|
||||
|
||||
auto keys = make_dpf_doerner_shelat(std::move(x0), std::move(x1),
|
||||
std::move(rng), std::move(spec));
|
||||
std::move(rng), std::move(spec), dpf::verifiable{});
|
||||
const auto & k0 = keys.first;
|
||||
const auto & k1 = keys.second;
|
||||
using key_type = std::decay_t<decltype(k0)>;
|
||||
|
|
@ -792,11 +878,19 @@ geneval_cmp_result geneval_cmp(InputT x0, InputT x1,
|
|||
if constexpr (key_type::cmp_block == 0)
|
||||
out.value_cw[level] = k0.value_cw(level);
|
||||
}
|
||||
detail::vdpf::init_proof(out.proof0, k0);
|
||||
detail::vdpf::init_proof(out.proof1, k1);
|
||||
auto path0 = make_basic_path_memoizer(k0);
|
||||
auto path1 = make_basic_path_memoizer(k1);
|
||||
for (auto it = begin; it != end; ++it)
|
||||
{
|
||||
out.party0.push_back(eval_point(dpf::cmp, k0, *it).raw());
|
||||
out.party1.push_back(eval_point(dpf::cmp, k1, *it).raw());
|
||||
out.party0.push_back(
|
||||
detail::incr::eval_cmp_point_impl(k0, *it, path0, &out.proof0).raw());
|
||||
out.party1.push_back(
|
||||
detail::incr::eval_cmp_point_impl(k1, *it, path1, &out.proof1).raw());
|
||||
}
|
||||
detail::vdpf::fold_output_binding(out.proof0, k0);
|
||||
detail::vdpf::fold_output_binding(out.proof1, k1);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -823,7 +917,7 @@ geneval_cmp_result geneval_cmp(arith_input_t, InputT x0, InputT x1,
|
|||
return out;
|
||||
|
||||
auto keys = make_dpf_doerner_shelat(arith_input, std::move(x0), std::move(x1),
|
||||
std::move(rng), std::move(spec));
|
||||
std::move(rng), std::move(spec), dpf::verifiable{});
|
||||
const auto & k0 = keys.first;
|
||||
const auto & k1 = keys.second;
|
||||
using key_type = std::decay_t<decltype(k0)>;
|
||||
|
|
@ -853,11 +947,19 @@ geneval_cmp_result geneval_cmp(arith_input_t, InputT x0, InputT x1,
|
|||
if constexpr (key_type::cmp_block == 0)
|
||||
out.value_cw[level] = k0.value_cw(level);
|
||||
}
|
||||
detail::vdpf::init_proof(out.proof0, k0);
|
||||
detail::vdpf::init_proof(out.proof1, k1);
|
||||
auto path0 = make_basic_path_memoizer(k0);
|
||||
auto path1 = make_basic_path_memoizer(k1);
|
||||
for (auto it = begin; it != end; ++it)
|
||||
{
|
||||
out.party0.push_back(eval_point(dpf::cmp, k0, *it).raw());
|
||||
out.party1.push_back(eval_point(dpf::cmp, k1, *it).raw());
|
||||
out.party0.push_back(
|
||||
detail::incr::eval_cmp_point_impl(k0, *it, path0, &out.proof0).raw());
|
||||
out.party1.push_back(
|
||||
detail::incr::eval_cmp_point_impl(k1, *it, path1, &out.proof1).raw());
|
||||
}
|
||||
detail::vdpf::fold_output_binding(out.proof0, k0);
|
||||
detail::vdpf::fold_output_binding(out.proof1, k1);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -903,6 +1005,124 @@ geneval_cmp_result geneval_cmp(arith_input_t, InputT x0, InputT x1,
|
|||
|
||||
/// @}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// @brief Copy party shares from a geneval result into caller buffers.
|
||||
template <typename Result, typename Buf0, typename Buf1>
|
||||
void geneval_fill_buffers(const Result & r, Buf0 & buf0, Buf1 & buf1)
|
||||
{
|
||||
const std::size_t n = r.party0.size();
|
||||
if (utils::size(buf0) < n || utils::size(buf1) < n)
|
||||
throw std::length_error("geneval: output buffer is too small");
|
||||
for (std::size_t i = 0; i < n; ++i)
|
||||
{
|
||||
buf0[i] = r.party0[i];
|
||||
buf1[i] = r.party1[i];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// @name Geneval into caller buffers
|
||||
/// @details Thin overloads that run the same trie walk, then copy party shares
|
||||
/// into `buf0` / `buf1` (same layout as `eval_interval` / `eval_sequence`
|
||||
/// output buffers). Memoizer arguments for the fused trie are internal;
|
||||
/// path memoizers live on `geneval_cmp` / `geneval_ic`.
|
||||
/// @{
|
||||
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
typename OutputT,
|
||||
typename RootSampler,
|
||||
typename PadRng,
|
||||
typename Buf0,
|
||||
typename Buf1>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto geneval_point(InputT x0, InputT x1, InputT query,
|
||||
ds_randomness<RootSampler, PadRng> rng, OutputT y, Buf0 & buf0, Buf1 & buf1)
|
||||
{
|
||||
auto r = geneval_point<InteriorPRG, ExteriorPRG>(std::move(x0),
|
||||
std::move(x1), query, std::move(rng), std::move(y));
|
||||
detail::geneval_fill_buffers(r, buf0, buf1);
|
||||
return r;
|
||||
}
|
||||
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
typename OutputT,
|
||||
typename RootSampler,
|
||||
typename PadRng,
|
||||
typename Buf0,
|
||||
typename Buf1>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto geneval_interval(InputT x0, InputT x1, InputT from, InputT to,
|
||||
ds_randomness<RootSampler, PadRng> rng, OutputT y, Buf0 & buf0, Buf1 & buf1)
|
||||
{
|
||||
auto r = geneval_interval<InteriorPRG, ExteriorPRG>(std::move(x0),
|
||||
std::move(x1), from, to, std::move(rng), std::move(y));
|
||||
detail::geneval_fill_buffers(r, buf0, buf1);
|
||||
return r;
|
||||
}
|
||||
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
typename OutputT,
|
||||
typename RootSampler,
|
||||
typename PadRng,
|
||||
typename Buf0,
|
||||
typename Buf1>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto geneval_full(InputT x0, InputT x1,
|
||||
ds_randomness<RootSampler, PadRng> rng, OutputT y, Buf0 & buf0, Buf1 & buf1)
|
||||
{
|
||||
auto r = geneval_full<InteriorPRG, ExteriorPRG>(std::move(x0),
|
||||
std::move(x1), std::move(rng), std::move(y));
|
||||
detail::geneval_fill_buffers(r, buf0, buf1);
|
||||
return r;
|
||||
}
|
||||
|
||||
/// \complexity O(n F) PRG expansions. n is `depth`. Each level expands every frontier node (two `expand` calls, one per share) and, while the secret path is live, one `prepare_level`. F is at most the number of distinct query leaves; the function rejects more than 2^20. Setup sorts the q query ids.
|
||||
/// \rounds No sockets. While the path is live, each level calls `prepare_level`, which samples one `ds_cw_pads` and two `ds_and_pads` and then `open_cw`.
|
||||
/// \communication none in this function.
|
||||
/// \preprocessing The `ds_randomness` tape: per live level, `ds_sample_cw` draws two 128-bit blocks and two bits, and each of the two AND pads is one `ds_sample_and`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
typename OutputT,
|
||||
typename RootSampler,
|
||||
typename PadRng,
|
||||
typename ForwardIterator,
|
||||
typename Buf0,
|
||||
typename Buf1>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto geneval_sequence(InputT x0, InputT x1, ForwardIterator begin,
|
||||
ForwardIterator end, ds_randomness<RootSampler, PadRng> rng, OutputT y,
|
||||
Buf0 & buf0, Buf1 & buf1)
|
||||
{
|
||||
auto r = geneval_sequence<InteriorPRG, ExteriorPRG>(std::move(x0),
|
||||
std::move(x1), begin, end, std::move(rng), std::move(y));
|
||||
detail::geneval_fill_buffers(r, buf0, buf1);
|
||||
return r;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
} // namespace dpf
|
||||
|
||||
#endif // LIBDPF_INCLUDE_DPF_GENEVAL_HPP__
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue