/// @file dpf/rss_seed.hpp /// @brief Pairwise PRG seeds for honest-majority 3PC RSS preprocessing. /// @details Parties 0,1,2 hold keys `k01`, `k12`, `k20`. Party `i` derives /// both components of a replicated random value, a zero-sharing, and /// the local cross term of an RSS multiply with no message. #ifndef LIBDPF_INCLUDE_DPF_RSS_SEED_HPP__ #define LIBDPF_INCLUDE_DPF_RSS_SEED_HPP__ #include #include #include #include #include #include #include "hedley/hedley.h" #include "dpf/buffered_prg.hpp" #include "dpf/prg_aes.hpp" #include "dpf/random.hpp" #include "dpf/secret_share.hpp" namespace dpf { namespace rss { using seed_block = typename prg::aes128::block_type; /// @brief Three pairwise master seeds. Role `i` holds seeds with `i±1 mod 3`. struct seed_bundle { seed_block k01{}; seed_block k12{}; seed_block k20{}; }; /// @brief Sample three independent pairwise seeds (dealer / setup). HEDLEY_WARN_UNUSED_RESULT inline seed_bundle sample_seed_bundle() { seed_bundle b; b.k01 = randomness::sample_master_seed(); b.k12 = randomness::sample_master_seed(); b.k20 = randomness::sample_master_seed(); return b; } /// @brief Party `me`'s view: the two seeds it shares with its neighbors. struct party_seeds { unsigned me = 0; seed_block with_prev{}; ///< shared with (me+2)%3 seed_block with_next{}; ///< shared with (me+1)%3 static party_seeds from_bundle(const seed_bundle & b, unsigned me) { if (me > 2) throw std::invalid_argument("rss party_seeds: me must be 0..2"); party_seeds s; s.me = me; if (me == 0) { s.with_prev = b.k20; s.with_next = b.k01; } else if (me == 1) { s.with_prev = b.k01; s.with_next = b.k12; } else { s.with_prev = b.k12; s.with_next = b.k20; } return s; } }; namespace detail { template T derive(seed_block master, std::uint32_t role, std::uint64_t index) { randomness::lane_table table(master); return table.value_at(role, index); } inline seed_block pair_seed(unsigned a, unsigned b, const seed_bundle & bundle) { const unsigned lo = a < b ? a : b; const unsigned hi = a < b ? b : a; if (lo == 0 && hi == 1) return bundle.k01; if (lo == 1 && hi == 2) return bundle.k12; if (lo == 0 && hi == 2) return bundle.k20; throw std::invalid_argument("rss pair_seed: bad pair"); } } // namespace detail /// @brief Replicated random `r` at `index`. Party `me` holds `(r_me, r_{me+1})`. template HEDLEY_WARN_UNUSED_RESULT replicated_share random_replicated0(const party_seeds & s, std::uint64_t index) { if (s.me != 0) throw std::invalid_argument("random_replicated0: wrong party"); // r0 from k20 (parties 2,0), r1 from k01 (parties 0,1) const T r0 = detail::derive(s.with_prev, 0, index); const T r1 = detail::derive(s.with_next, 1, index); return replicated_share::from_raw(r0, r1); } template HEDLEY_WARN_UNUSED_RESULT replicated_share random_replicated1(const party_seeds & s, std::uint64_t index) { if (s.me != 1) throw std::invalid_argument("random_replicated1: wrong party"); const T r1 = detail::derive(s.with_prev, 1, index); const T r2 = detail::derive(s.with_next, 2, index); return replicated_share::from_raw(r1, r2); } template HEDLEY_WARN_UNUSED_RESULT replicated_share random_replicated2(const party_seeds & s, std::uint64_t index) { if (s.me != 2) throw std::invalid_argument("random_replicated2: wrong party"); const T r2 = detail::derive(s.with_prev, 2, index); const T r0 = detail::derive(s.with_next, 0, index); return replicated_share::from_raw(r2, r0); } /// @brief Party-erased random replicated share. template HEDLEY_WARN_UNUSED_RESULT std::pair random_replicated_components(const party_seeds & s, std::uint64_t index) { if (s.me == 0) { auto sh = random_replicated0(s, index); return {sh.own, sh.next}; } if (s.me == 1) { auto sh = random_replicated1(s, index); return {sh.own, sh.next}; } auto sh = random_replicated2(s, index); return {sh.own, sh.next}; } /// @brief Zero-sharing for reshare: party `i` holds `z_i` with sum zero. /// @details `z_i = r_i - r_{i-1}` where `r` is a common pairwise stream on /// each edge; the three differences cancel. template HEDLEY_WARN_UNUSED_RESULT T zero_share(const party_seeds & s, std::uint64_t index) { // On edge with_next: parties me and next share stream role 0 → value α // On edge with_prev: parties me and prev share stream role 0 → value β // z_me = α - β so sum over the ring is zero. const T alpha = detail::derive(s.with_next, 10u + s.me, index); const T beta = detail::derive(s.with_prev, 10u + ((s.me + 2) % 3), index); return static_cast(alpha - beta); } /// @brief Local y-component of an RSS multiply before neighbor refresh. /// @details Party `i` holds `(x_i, x_{i+1})` and `(y_i, y_{i+1})`. The local /// product factor is `x_i*y_i + x_i*y_{i+1} + x_{i+1}*y_i` plus a /// zero-mask so the three factors sum to `xy` after refresh. template HEDLEY_WARN_UNUSED_RESULT T rss_mul_local(const party_seeds & s, T x_own, T x_next, T y_own, T y_next, std::uint64_t index) { const T cross = static_cast( x_own * y_own + x_own * y_next + x_next * y_own); const T mask = zero_share(s, index); return static_cast(cross + mask); } /// @brief Full dealer view: derive every party's components for tests. template struct replicated_triple { replicated_share p0; replicated_share p1; replicated_share p2; }; template HEDLEY_WARN_UNUSED_RESULT replicated_triple random_replicated_all(const seed_bundle & b, std::uint64_t index) { auto s0 = party_seeds::from_bundle(b, 0); auto s1 = party_seeds::from_bundle(b, 1); auto s2 = party_seeds::from_bundle(b, 2); return replicated_triple{ random_replicated0(s0, index), random_replicated1(s1, index), random_replicated2(s2, index)}; } } // namespace rss } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_RSS_SEED_HPP__