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

@ -77,6 +77,27 @@ template <typename OutputT,
static constexpr std::size_t block_length_of_leaf_v
= block_length_of_leaf<OutputT, NodeT>::value;
// ---------------------------------------------------------------------------
// dpf::blob<N> stretched byte leaves.
// A `dpf::blob<N>` is an XOR share of `N` bytes and is *never* packed several
// per node: Boyle packing keeps `lg(outputs_per_leaf) = 0` so the tree depth
// follows the input bitlength (Express `genDPF`). Instead it stretches the
// final seed over `ceil(8N / lambda)` exterior blocks in counter mode. These
// specializations override the generic `is_packable` heuristic, which would
// otherwise pack small blobs (e.g. `blob<1>`, whose 8 bits divide `lambda`).
// See dpf/blob.hpp.
template <std::size_t N,
typename NodeT>
struct outputs_per_leaf<dpf::blob<N>, NodeT>
: public std::integral_constant<std::size_t, 1> { };
template <std::size_t N,
typename NodeT>
struct block_length_of_leaf<dpf::blob<N>, NodeT>
: public std::integral_constant<std::size_t,
utils::quotient_ceiling(N * 8,
utils::bitlength_of_output_v<NodeT, NodeT>)> { };
template <typename OutputT,
typename NodeT,
typename InputT>
@ -205,6 +226,10 @@ struct beaver<true, NodeT, OutputT> final
OutputT output_blind;
LeafT vector_blind;
LeafT blinded_vector;
/// @brief Keygen pad `vector_blind_i · output_blind_{1-i}` so a later
/// `begin_update` can open a fresh naked delta without replaying
/// the zero-payload correction word.
LeafT assign_pad{};
};
template <typename NodeT,
@ -287,6 +312,33 @@ constexpr auto * leaf_blocks(LeafT & leaf) noexcept
return leaf.data();
}
/// @brief After a PRG fills `leaf`, map curve-point types through `from_seed`.
template <typename Concrete, typename = void>
struct is_curve_leaf_encode : std::false_type {};
template <typename Concrete>
struct is_curve_leaf_encode<Concrete, std::void_t<
std::bool_constant<Concrete::dpf_curve_point>,
decltype(Concrete::from_seed(static_cast<const void *>(nullptr),
std::size_t{0})),
std::integral_constant<std::size_t, Concrete::encoded_size>,
decltype(std::declval<const Concrete &>().bytes())>>
: std::bool_constant<Concrete::dpf_curve_point> {};
template <typename Concrete, typename Leaf>
HEDLEY_ALWAYS_INLINE
void encode_curve_leaf_mask(Leaf & leaf) noexcept
{
if constexpr (is_curve_leaf_encode<Concrete>::value)
{
const Concrete pt = Concrete::from_seed(
std::addressof(leaf), sizeof(leaf));
leaf = Leaf{};
std::memcpy(std::addressof(leaf), pt.bytes(),
Concrete::encoded_size);
}
}
template <typename ExteriorPRG,
std::size_t I,
typename OutputsTuple,
@ -295,6 +347,7 @@ auto make_leaf_mask_inner(const InteriorBlock & seed, std::size_t pos_base = 0)
{
using node_type = typename ExteriorPRG::block_type;
using output_type = std::tuple_element_t<I, OutputsTuple>;
using concrete = concrete_type_t<output_type>;
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
using leaf_type = dpf::leaf_node_t<node_type, output_type>;
@ -305,6 +358,7 @@ HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto seed_ = utils::to_exterior_node<node_type>(seed);
ExteriorPRG::eval(seed_, leaf_blocks<node_type>(output), count,
static_cast<psnip_uint32_t>(pos));
encode_curve_leaf_mask<concrete>(output);
return output;
HEDLEY_PRAGMA(GCC diagnostic pop)
@ -370,6 +424,23 @@ auto make_leaves_impl(InputT x, const ExteriorBlock & seed0, const ExteriorBlock
make_leaf<ExteriorPRG, Is>(x, seed0, seed1, sign, pos_base, ys...)...);
}
namespace beavers
{
/// @brief Fill wildcard leaf scale blinds from `sample_scale`.
/// @tparam Concrete lane type
/// @tparam LeafT packed leaf type
/// @tparam Sample ring sampler
template <typename Concrete, typename LeafT, typename Sample>
void fill_wildcard_scale_blinds(Concrete & out0, Concrete & out1, LeafT & vec0,
LeafT & vec1, std::size_t nlanes, Sample && rng);
template <typename Concrete, typename LeafT>
void fill_wildcard_scale_blinds(Concrete & out0, Concrete & out1, LeafT & vec0,
LeafT & vec1, std::size_t nlanes);
} // namespace beavers
template <typename ExteriorPRG,
typename InputT,
typename ExteriorBlock,
@ -435,38 +506,43 @@ HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
// secret share the value
dpf::uniform_fill(leaf0);
leaf1 = dpf::subtract_leaf<concrete_type>(leaf, leaf0);
// also initialize the beavers
if constexpr(!dpf::utils::has_characteristic_two_v<concrete_type>
|| dpf::outputs_per_leaf_v<concrete_type, node_type> > 1)
// Always plant a scale Beaver, including full-width XOR
// (char-2, one lane). Skipping it left blinds at zero so
// online assign exchanged beta in the clear.
dpf::leaf_node_t<node_type, concrete_type> vector;
// XOR/AND leaf groups use the all-ones word as unit, not ±1.
// Check the OUTPUT type: input may be modint while the
// leaf is xor_wrapper (wildcard XOR payload). IEEE
// float/double leaves are the same bitwise group.
if constexpr(utils::is_xor_wrapper_v<std::decay_t<decltype(x)>> == true
|| utils::is_xor_wrapper_v<concrete_type> == true
|| std::is_same_v<concrete_type, float>
|| std::is_same_v<concrete_type, double>)
{
dpf::leaf_node_t<node_type, concrete_type> vector;
// XOR-group multiply is AND, whose unit is ~0, not ±1.
// Check the OUTPUT type: input may be modint while the
// leaf is xor_wrapper (wildcard XOR payload).
if constexpr(utils::is_xor_wrapper_v<std::decay_t<decltype(x)>> == true
|| utils::is_xor_wrapper_v<concrete_type> == true)
{
vector = make_naked_leaf<node_type>(x, concrete_type(~0));
}
else
{
vector = make_naked_leaf<node_type>(x, concrete_type(2*sign-1));
}
uniform_fill(beaver0.output_blind);
uniform_fill(beaver0.vector_blind);
uniform_fill(beaver1.output_blind);
uniform_fill(beaver1.vector_blind);
beaver0.blinded_vector = dpf::add_leaf<concrete_type>(vector, beaver1.vector_blind);
beaver1.blinded_vector = dpf::add_leaf<concrete_type>(vector, beaver0.vector_blind);
leaf0 = dpf::add_leaf<concrete_type>(leaf0,
dpf::multiply_leaf(beaver0.vector_blind, beaver1.output_blind));
leaf1 = dpf::add_leaf<concrete_type>(leaf1,
dpf::multiply_leaf(beaver1.vector_blind, beaver0.output_blind));
vector = make_naked_leaf<node_type>(x,
dpf::leaf_group_one<concrete_type>());
}
else
{
vector = make_naked_leaf<node_type>(x, concrete_type(2*sign-1));
}
constexpr std::size_t nlanes =
dpf::outputs_per_leaf_v<concrete_type, node_type>;
dpf::beavers::fill_wildcard_scale_blinds(
beaver0.output_blind, beaver1.output_blind,
beaver0.vector_blind, beaver1.vector_blind,
nlanes > 0 ? nlanes : std::size_t{1});
beaver0.blinded_vector = dpf::add_leaf<concrete_type>(vector, beaver1.vector_blind);
beaver1.blinded_vector = dpf::add_leaf<concrete_type>(vector, beaver0.vector_blind);
beaver0.assign_pad = dpf::multiply_leaf(
beaver0.vector_blind, beaver1.output_blind);
beaver1.assign_pad = dpf::multiply_leaf(
beaver1.vector_blind, beaver0.output_blind);
leaf0 = dpf::add_leaf<concrete_type>(leaf0, beaver0.assign_pad);
leaf1 = dpf::add_leaf<concrete_type>(leaf1, beaver1.assign_pad);
}
else
{