/// @file dpf/dpf3_ds.hpp /// @brief Dual-spine Doerner–Shelat generation of (2,3) point keys. /// @note Two independent openings, one per spine of Zyskind, Yanai, and Pentland (ePrint 2024/1658, Figure 3). Each opening follows Doerner and shelat, CCS 2017 (ePrint 2017/827). /// @details Runs two independent two-party DS walks (spines A and B) with /// Fig-3 `τ` payloads, then assembles the three evaluator keys via /// `assemble_from_spines`. Matches honest-dealer `make_dpf3` on the /// opened point `x0 ⊕ x1` (after the signed-MSB flip on share 0). /// @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. #ifndef LIBDPF_INCLUDE_DPF_DPF3_DS_HPP__ #define LIBDPF_INCLUDE_DPF_DPF3_DS_HPP__ #include #include #include #include "hedley/hedley.h" #include "dpf/dpf3.hpp" #include "dpf/fp61.hpp" #include "dpf/incremental.hpp" #include "dpf/placement.hpp" #include "dpf/prg_aes.hpp" #include "dpf/verifiable.hpp" #include "dpf/wildcard.hpp" namespace dpf { namespace detail { namespace dpf3_impl { template auto make_point3_ds(InputT x0, InputT x1, fp61 beta) { using X = xor61; const InputT alpha = open_xor_point(x0, x1); const tau_quad t = sample_taus(beta); const X payload_a = t.t0 + t.t1; const X payload_b = t.t2 + t.t3; if constexpr (Updatable) { // Classic DS rejects wildcards; the incremental path plants beaver // leaves. Inner verifiable tags match the outer `Verifiable` flag. auto A = [&] { if constexpr (Verifiable) return dpf::make_dpf_doerner_shelat( x0, x1, dpf::wildcard_value{}, dpf::verifiable{}); else return dpf::make_dpf_doerner_shelat( x0, x1, dpf::wildcard_value{}); }(); auto B = [&] { if constexpr (Verifiable) return dpf::make_dpf_doerner_shelat( x0, x1, dpf::wildcard_value{}, dpf::verifiable{}); else return dpf::make_dpf_doerner_shelat( x0, x1, dpf::wildcard_value{}); }(); assign_xor_payload(A.first, A.second, payload_a); assign_xor_payload(B.first, B.second, payload_b); return assemble_from_spines( std::move(A.first), std::move(A.second), std::move(B.first), std::move(B.second), t, alpha); } else { auto A = [&] { if constexpr (Verifiable) return dpf::make_dpf_doerner_shelat( x0, x1, payload_a, dpf::verifiable{}); else return dpf::make_dpf_doerner_shelat( x0, x1, payload_a); }(); auto B = [&] { if constexpr (Verifiable) return dpf::make_dpf_doerner_shelat( x0, x1, payload_b, dpf::verifiable{}); else return dpf::make_dpf_doerner_shelat( x0, x1, payload_b); }(); return assemble_from_spines( std::move(A.first), std::move(A.second), std::move(B.first), std::move(B.second), t, alpha); } } } // namespace dpf3_impl } // namespace detail /// @brief Dual-spine Doerner–Shelat (2,3) keys for XOR shares of `α`. /// @details `α = x0 ⊕ x1` after the signed-MSB flip on `x0`. Tags match /// `make_dpf3`: `verifiable`, `extractable`, and `updatable`, any order. /// \complexity O(n) time. One `ds_advance_level` per level: two PRG expansions and one `prepare_level`. n is `depth`. /// \rounds No sockets. This is the in-process transcript. A networked walk is `dpf::party::dist::point_party`. /// \communication none here. `local_cw_protocol` opens the correction word locally. /// \preprocessing Per level, `prepare_level` draws one `ds_cw_pads` (two parties × a 128-bit rand, a 128-bit gamma, and a bit) and two `ds_and_pads`. Arithmetic inputs also run a carry chain of n-1 bit-AND triples in `encode_walk_shares`. template HEDLEY_WARN_UNUSED_RESULT auto make_dpf3_doerner_shelat(InputT x0, InputT x1, fp61 beta, Tags ...tags) { using flags = detail::dpf3_impl::tag_flags; static_assert(flags::known, "make_dpf3_doerner_shelat tags are verifiable, extractable, updatable"); static_assert(sizeof...(Tags) == flags::counted, "make_dpf3_doerner_shelat: repeated tag"); (void)std::initializer_list{((void)tags, 0)...}; return detail::dpf3_impl::make_point3_ds(x0, x1, beta); } } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_DPF3_DS_HPP__