libdpf/include/dpf/grow_ds.hpp
Ryan Henry 0d22946a0e 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>
2026-09-28 05:59:19 -06:00

132 lines
5.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// @file dpf/grow_ds.hpp
/// @brief Doerner–Shelat interactive / joint grow: `extend_ds` and
/// `add_output_ds` on path-memoizer frontiers.
/// @details Joint (2+1 / local) view holds both keys and both memoizers. One
/// correction-word round uses `ds_advance_level`; leaf planting reuses
/// dealer `grow_impl` with the opened CW. A socket backend swaps in a
/// `CwProtocol` that exchanges blinds without revealing the peer seed.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
#ifndef LIBDPF_INCLUDE_DPF_GROW_DS_HPP__
#define LIBDPF_INCLUDE_DPF_GROW_DS_HPP__
#include <array>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <utility>
#include "hedley/hedley.h"
#include "dpf/doerner_shelat.hpp"
#include "dpf/grow.hpp"
#include "dpf/path_memoizer.hpp"
#include "dpf/utils.hpp"
namespace dpf
{
/// @brief One interactive interior level from memoizer frontiers, then plant
/// `specs` via the same assembly as dealer `extend`.
/// @details `x0` / `x1` are XOR shares of the programmed point (use `(x, 0)` in
/// a local joint test). Memoizers must already be filled for the clear
/// point `x0 ⊕ x1` through the old depth.
/// \complexity One `ds_advance_level` (two PRG expands + `prepare_level` /
/// `open_cw` / AND opens) plus the same leaf plants as dealer
/// `extend`. No O(d) rewalk when memoizers are warm.
/// \rounds One interactive CW round for the new level (local_cw_protocol opens
/// in-process; a socket `CwProtocol` is one peer exchange round).
/// \communication Local: none on the wire. Networked: one level's blinds, CW
/// shares, and advice (same shape as one `point_party` level),
/// plus leaf pads when planting.
template <typename K0, typename K1, typename Memo0, typename Memo1,
typename InputT, typename CwProtocol, typename... Specs>
HEDLEY_WARN_UNUSED_RESULT
auto extend_ds(const K0 & k0, const K1 & k1, Memo0 & m0, Memo1 & m1, InputT x0,
InputT x1, CwProtocol & proto, Specs &&... specs)
{
using old_key = detail::grow_impl::bare_key_t<K0>;
static_assert(std::is_same_v<old_key, detail::grow_impl::bare_key_t<K1>>,
"extend_ds: both keys must have the same type");
using input_type = typename old_key::input_type;
using node = typename old_key::interior_node;
using interior = typename old_key::interior_prg;
input_type xx0 = static_cast<input_type>(x0);
input_type xx1 = static_cast<input_type>(x1);
utils::flip_msb_if_signed_integral(xx0);
// Party 1 share is not MSB-flipped in DS (same as make_dpf_doerner_shelat).
const input_type x = utils::xor_input_shares(xx0, xx1);
const old_key & bk0 = static_cast<const old_key &>(k0);
const old_key & bk1 = static_cast<const old_key &>(k1);
const bool bit = detail::grow_impl::bit_at(x, old_key::depth);
node s0{};
node s1{};
std::array<bool, old_key::depth == 0 ? 1 : old_key::depth> path{};
detail::grow_impl::frontier_from_memos(bk0, bk1, m0, m1, x, old_key::depth, s0,
s1, old_key::depth == 0 ? nullptr : path.data());
detail::ds_gen_state<node> st;
st.init(s0, s1);
const std::size_t level = old_key::depth;
const std::size_t new_depth = old_key::depth + 1;
auto mask = old_key::msb_mask;
for (std::size_t i = 0; i < level; ++i)
mask >>= 1;
node cw{};
psnip_uint8_t advice = 0;
detail::ds_advance_level<interior>(st, xx0, xx1, mask, level, new_depth,
proto, cw, advice);
node ns0 = st.seed0();
node ns1 = st.seed1();
return detail::grow_impl::grow_impl<true, true>(bk0, bk1, &m0, &m1, bit, x,
true, &cw, &advice, &ns0, &ns1, std::forward<Specs>(specs)...);
}
/// @brief Plant outputs on existing levels using memoizer seeds. Joint leaf
/// construction matches dealer `add_output`; `proto.open_leaf_group` is
/// invoked so a non-local protocol can hide the clear point.
/// \complexity O(1) frontier reads plus leaf plants. No new interior CW.
/// \rounds Leaf-open only (local: one `open_leaf_group` callback; networked:
/// the leaf pad / mux pattern of `point_party`).
/// \communication none for `local_cw_protocol`; otherwise leaf pads and the
/// leaf CW open.
template <typename K0, typename K1, typename Memo0, typename Memo1,
typename InputT, typename CwProtocol, typename... Specs>
HEDLEY_WARN_UNUSED_RESULT
auto add_output_ds(const K0 & k0, const K1 & k1, Memo0 & m0, Memo1 & m1,
InputT x0, InputT x1, CwProtocol & proto, Specs &&... specs)
{
using old_key = detail::grow_impl::bare_key_t<K0>;
using input_type = typename old_key::input_type;
const old_key & bk0 = static_cast<const old_key &>(k0);
const old_key & bk1 = static_cast<const old_key &>(k1);
input_type xx0 = static_cast<input_type>(x0);
input_type xx1 = static_cast<input_type>(x1);
utils::flip_msb_if_signed_integral(xx0);
input_type x{};
proto.open_leaf_group(xx0, xx1, [&](input_type sx0, input_type sx1) {
x = utils::xor_input_shares(sx0, sx1);
});
return detail::grow_impl::grow_impl<false, true>(bk0, bk1, &m0, &m1,
/*bit=*/false, x, false,
static_cast<typename old_key::interior_node *>(nullptr),
static_cast<psnip_uint8_t *>(nullptr),
static_cast<typename old_key::interior_node *>(nullptr),
static_cast<typename old_key::interior_node *>(nullptr),
std::forward<Specs>(specs)...);
}
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_GROW_DS_HPP__