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:
Ryan Henry 2026-09-28 05:59:19 -06:00
parent 695f8e84f7
commit 0d22946a0e
1835 changed files with 170291 additions and 2849 deletions

View file

@ -17,6 +17,7 @@
#include <bitset>
#include <atomic>
#include "dpf/experiment_note.hpp"
#include "dpf/prg_aes.hpp"
#include "dpf/tree_traits.hpp"
#include "dpf/wildcard.hpp"
@ -35,6 +36,12 @@ namespace dpf
#ifdef LIBDPF_HAS_ASIO
namespace asio
{
/// @brief Exchange a wildcard output share and install the opened leaf.
/// @see dpf::leaf_wrapper
/// \complexity O(leaf bytes) for the Beaver leaf arithmetic, plus the transfers.
/// \rounds 2. Write/read the blinded output share, then write/read the leaf share (`async_assign_wildcard_output`).
/// \communication `sizeof(output_type)` plus `sizeof(leaf_type)` each way.
/// \preprocessing The leaf Beaver triple (`vector_blind`, `output_blind`, `blinded_vector`) was stored at keygen.
template <std::size_t I,
typename PeerT,
typename DpfKey,
@ -251,14 +258,14 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
const leaf_tuple & leaves,
const beaver_tuple & beavers,
input_type offset_share)
: root_{root},
: leaf_nodes(get_wrappers(leaves, beavers)),
offset_x{offset_share},
root_{root},
correction_words_{correction_words},
correction_advice_{correction_advice},
mutable_wildcard_mask_{dpf::utils::make_bitset(dpf::is_wildcard_v<OutputT>,
dpf::is_wildcard_v<OutputTs>...)},
leaf_nodes(get_wrappers(leaves, beavers)),
common_part_hash_{utils::get_common_part_hash(correction_words_, correction_advice_, leaf_nodes, wildcard_mask)},
offset_x{offset_share}
common_part_hash_{utils::get_common_part_hash(correction_words_, correction_advice_, leaf_nodes, wildcard_mask)}
{ }
classic_dpf_key_impl(const classic_dpf_key_impl &) = default;
classic_dpf_key_impl(classic_dpf_key_impl &&) = default;
@ -410,6 +417,29 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
return traverse_exterior<I>(node, std::get<I>(leaf_nodes).get());
}
/// @brief Eight one-block leaves. One `eval_x8` instead of eight `eval` calls.
template <std::size_t I = 0, typename Out>
void traverse_exterior_x8(const interior_node * HEDLEY_RESTRICT nodes,
Out * HEDLEY_RESTRICT out) const noexcept
{
using output_type = std::tuple_element_t<I, concrete_outputs_tuple>;
using block = typename exterior_prg::block_type;
alignas(64) block seeds[8];
alignas(64) block masks[8];
for (std::size_t t = 0; t < 8; ++t)
seeds[t] = utils::to_exterior_node<block>(unset_lo_2bits(nodes[t]));
constexpr auto pos = dpf::block_offset_of_leaf_v<I, block,
concrete_outputs_tuple>;
exterior_prg::eval_x8(seeds, masks, static_cast<psnip_uint32_t>(pos));
const auto & cw = std::get<I>(leaf_nodes).get();
for (std::size_t t = 0; t < 8; ++t)
{
encode_curve_leaf_mask<concrete_type_t<output_type>>(masks[t]);
out[t] = dpf::subtract_leaf<output_type>(
dpf::get_if_lo_bit(cw, nodes[t]), masks[t]);
}
}
leaf_wrapper_tuple leaf_nodes;
offset_type offset_x;
static constexpr std::array<bool, sizeof...(OutputTs)+1> wildcard_mask{dpf::is_wildcard_v<OutputT>,
@ -670,6 +700,35 @@ struct cmp_storage
}
}
/// @brief `mix(base, coeff)` at every value CW, then install `addend`.
/// @tparam Mix word rewriter
/// @param mix combines one stored base word with its integer coefficient word
/// @param addend this party's share of the constant payload
template <typename Mix>
void assign_payload(Mix mix, value_cw_word addend)
{
static_assert(Wild,
"assign_cmp on a key whose comparison payload is not a wildcard");
if constexpr (Wild)
{
for (std::size_t i = 0; i < Depth; ++i)
value_cw_[i] = mix(value_cw_[i], wild_.value_cw_coeff[i]);
if constexpr (Blocked)
{
for (std::size_t i = 0; i < TailLen; ++i)
tail_[i] = mix(tail_[i], wild_.tail_coeff[i]);
}
cw_last_ = mix(cw_last_, wild_.cw_last_coeff);
if constexpr (Idcf)
{
for (std::size_t i = 0; i < prefix_cw_len; ++i)
prefix_cw_[i] = mix(prefix_cw_[i], wild_.prefix_cw_coeff[i]);
}
cmp_addend_ = addend;
wild_.assigned = true;
}
}
/// @brief Per-level δ coefficients for a wildcard comparison. Empty when the
/// payload is concrete.
/// @return the coefficient table
@ -767,6 +826,7 @@ struct incr_key_base
using interior_node = typename InteriorPRG::block_type;
using exterior_node = typename ExteriorPRG::block_type;
using input_type = dpf::concrete_type_t<InputT>;
using raw_input_type = InputT;
using placed_tuple = PlacedTuple;
using node_type = exterior_node;
static constexpr std::size_t cmp_depth = CmpDepth;
@ -797,10 +857,15 @@ struct incr_key_base
static constexpr std::size_t cmp_tail =
(CmpBlock == 0 || cmp_q == 0) ? 0 : (std::size_t{1} << cmp_q);
/// @brief Narrowest unsigned word that holds `cmp_out_bits` bits (1 byte for a
/// bit / ≤8-bit payload, 2 for ≤16, 4 for ≤32, 8 for ≤64). Value CWs and
/// the addend share are stored in this word.
using value_cw_word = utils::integral_type_from_bitlength_t<
(CmpOutBits == 0 ? std::size_t{1} : CmpOutBits)>;
/// bit / ≤8-bit payload, 2 for ≤16, 4 for ≤32, 8 for ≤64). Payloads wider
/// than 256 bits are stored as their raw bytes. Value CWs and the addend
/// share use this word.
static constexpr std::size_t cmp_word_bits =
CmpOutBits == 0 ? std::size_t{1} : CmpOutBits;
using value_cw_word = std::conditional_t<
(cmp_word_bits <= 256),
utils::integral_type_from_bitlength_t<cmp_word_bits>,
std::array<std::uint8_t, (cmp_word_bits + 7) / 8>>;
static constexpr std::size_t num_outputs = std::tuple_size_v<PlacedTuple>;
static constexpr std::size_t input_bits = utils::bitlength_of_v<input_type>;
@ -835,12 +900,15 @@ struct incr_key_base
/// @brief Multi-level / comparison keys route through the slot-aware eval path.
/// Classic-shaped packs (every slot at full input width, no cmp) keep the
/// classic `eval_*` fast path even when verifiable/extractable phantoms are
/// present.
/// present. `eq` / `eq_at` with a public `if_false` addend also take the
/// slot-aware path so party 0 can absorb that addend.
static constexpr bool is_multilevel = [] {
if constexpr (CmpDepth > 0)
return true;
if constexpr (num_outputs == 0)
return true;
else if constexpr (detail::incr::any_public_addend_v<PlacedTuple>)
return true;
else
{
for (std::size_t i = 0; i < num_outputs; ++i)
@ -867,6 +935,20 @@ struct incr_key_base
template <std::size_t I>
using concrete_output_type = concrete_type_t<output_type_t<I>>;
private:
template <std::size_t... Is>
static auto outputs_tuple_type(std::index_sequence<Is...>)
-> std::tuple<output_type_t<Is>...>;
template <std::size_t... Is>
static auto concrete_outputs_tuple_type(std::index_sequence<Is...>)
-> std::tuple<concrete_output_type<Is>...>;
public:
using outputs_tuple = decltype(outputs_tuple_type(
std::make_index_sequence<num_outputs>{}));
using concrete_outputs_tuple = decltype(concrete_outputs_tuple_type(
std::make_index_sequence<num_outputs>{}));
template <std::size_t I>
static constexpr std::size_t lg_outputs_per_leaf_of =
(num_outputs > 0) ? meta[I].lg_opl : 0;
@ -899,6 +981,23 @@ struct incr_key_base
static constexpr auto wildcard_mask =
wildcard_mask_tuple(std::make_index_sequence<num_outputs>{});
template <std::size_t... Is>
static constexpr std::array<bool, num_outputs>
wildcard_mask_array(std::index_sequence<Is...>)
{
return {{std::get<Is>(wildcard_mask)...}};
}
static constexpr auto wildcard_bits =
wildcard_mask_array(std::make_index_sequence<num_outputs>{});
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
constexpr bool is_wildcard(std::size_t i) const noexcept
{
return i < num_outputs && wildcard_bits[i];
}
static constexpr std::size_t deepest_prefix = [] {
if constexpr (num_outputs == 0)
return CmpDepth;
@ -938,6 +1037,14 @@ struct incr_key_base
using addend_tuple = decltype(addend_tuple_t(
std::make_index_sequence<num_outputs>{}));
static value_cw_word cw_word_from_u64(std::uint64_t v)
{
if constexpr (std::is_integral_v<value_cw_word>)
return static_cast<value_cw_word>(v);
else
return value_cw_word{};
}
incr_key_base(interior_node root,
const correction_words_array & correction_words,
const correction_advice_array & correction_advice,
@ -951,13 +1058,13 @@ struct incr_key_base
correction_seeds_array correction_seeds = {})
: leaf_nodes{std::move(leaves)},
offset_x{offset_share},
cmp_store_{cmp, value_cws,
static_cast<value_cw_word>(cw_last_in),
static_cast<value_cw_word>(cmp_addend_in),
value_cw_coeff,
static_cast<value_cw_word>(cw_last_coeff_in),
tail_in, tail_coeff_in, prefix_in, prefix_coeff_in},
public_addends{std::move(addends)},
cmp_store_{cmp, value_cws,
cw_word_from_u64(cw_last_in),
cw_word_from_u64(cmp_addend_in),
value_cw_coeff,
cw_word_from_u64(cw_last_coeff_in),
tail_in, tail_coeff_in, prefix_in, prefix_coeff_in},
root_{root},
correction_words_{correction_words},
correction_advice_{correction_advice},
@ -1027,6 +1134,11 @@ struct incr_key_base
{
cmp_store_.assign_group(delta, addend);
}
template <typename Mix>
void assign_cmp_payload(Mix mix, value_cw_word addend)
{
cmp_store_.assign_payload(std::move(mix), addend);
}
HEDLEY_NO_THROW
const prefix_cw_array & prefix_cws() const noexcept
{
@ -1125,9 +1237,12 @@ struct incr_key_base
tree::traverse01_x4(parents, cw0, cw1, left, right, is_last);
}
template <std::size_t I = 0>
template <std::size_t I = 0, typename LeafT>
HEDLEY_NO_THROW
auto traverse_exterior(const interior_node & node) const noexcept
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_exterior(const interior_node & node,
const LeafT & correction_word) noexcept
{
static_assert(num_outputs > 0, "cmp-only key has no exterior outputs");
using Out = concrete_output_type<I>;
@ -1154,8 +1269,50 @@ struct incr_key_base
static_cast<psnip_uint32_t>(count),
static_cast<psnip_uint32_t>(pos));
}
encode_curve_leaf_mask<Out>(mask);
return dpf::subtract_leaf<Out>(
dpf::get_if_lo_bit(std::get<I>(leaf_nodes).get(), node), mask);
dpf::get_if_lo_bit(correction_word, node), mask);
}
template <std::size_t I = 0>
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
auto traverse_exterior(const interior_node & node) const noexcept
{
static_assert(num_outputs > 0, "cmp-only key has no exterior outputs");
return traverse_exterior<I>(node, std::get<I>(leaf_nodes).get());
}
/// @brief Eight one-block leaves, or eight scalar leaves when the PRG is special.
template <std::size_t I = 0, typename Out>
void traverse_exterior_x8(const interior_node * HEDLEY_RESTRICT nodes,
Out * HEDLEY_RESTRICT out) const noexcept
{
const auto & cw = std::get<I>(leaf_nodes).get();
if constexpr (IsExtractable || meta[I].block_len != 1)
{
for (std::size_t t = 0; t < 8; ++t)
out[t] = traverse_exterior<I>(nodes[t], cw);
return;
}
else
{
using OutT = concrete_output_type<I>;
using block = exterior_node;
alignas(64) block seeds[8];
alignas(64) block masks[8];
constexpr auto pos = meta[I].pos_base
+ meta[I].index_in_group * meta[I].block_len;
for (std::size_t t = 0; t < 8; ++t)
seeds[t] = utils::to_exterior_node<block>(unset_lo_2bits(nodes[t]));
exterior_prg::eval_x8(seeds, masks, static_cast<psnip_uint32_t>(pos));
for (std::size_t t = 0; t < 8; ++t)
{
encode_curve_leaf_mask<OutT>(masks[t]);
out[t] = dpf::subtract_leaf<OutT>(
dpf::get_if_lo_bit(cw, nodes[t]), masks[t]);
}
}
}
leaf_wrapper_tuple leaf_nodes;
@ -1383,6 +1540,16 @@ using incr_dpf_key_of_t = typename incr_dpf_key_of<InteriorPRG, ExteriorPRG,
} // namespace incr
} // namespace detail
/// @brief One uniform interior node. The default root draw for distributed dealers.
template <typename Node>
struct uniform_node_sampler
{
Node operator()() const
{
return dpf::uniform_sample<Node>();
}
};
template <typename PRG>
struct pseudorandom_root_sampler
{
@ -1390,7 +1557,10 @@ struct pseudorandom_root_sampler
pseudorandom_root_sampler(
root_type && seed = dpf::uniform_sample<root_type>())
: seed_{seed}, counter_{0} { }
: seed_{seed}, counter_{0}
{
note_experiment_seed("pseudorandom_root_sampler", seed_);
}
root_type operator()(psnip_uint32_t i) const
{
@ -1528,6 +1698,13 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
} // namespace detail
/// @brief Dealer keygen. Returns the two party keys.
/// @param args plaintext point and payloads
/// @param root_sampler draws the interior roots
/// @return a `party_key` pair
/// @note Signed domains flip the MSB before the walk (`flip_msb_if_signed_integral`).
/// @see dpf::eval_point
/// \complexity O(n) time and O(n) key size. n is the domain bitlength (`depth`). The loop does two interior PRG expansions and writes one correction word per level, then builds one exterior leaf per output.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,