/// @file dpf/shamir3.hpp /// @brief Degree-1 Shamir sharing over `fp61` for three evaluators. /// @details The `(K,N) = (2,3)` case of `shamir::deal` / `shamir::reconstruct`. /// Parties are indexed `1`, `2`, and `3`. A share of secret `s` is /// `s_i = s + a·i` for a uniform slope `a`. Any two parties /// reconstruct by Lagrange. Embeddings move between `fp61` and a /// 61-bit XOR string for the (2,3) point construction of /// Zyskind, Yanai, and Pentland, ePrint 2024/1658. /// @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_SHAMIR3_HPP__ #define LIBDPF_INCLUDE_DPF_SHAMIR3_HPP__ #include #include #include #include #include #include "hedley/hedley.h" #include "dpf/fp61.hpp" #include "dpf/random.hpp" #include "dpf/secret_share.hpp" #include "dpf/xor_wrapper.hpp" namespace dpf { namespace shamir3 { /// @brief XOR string wide enough for an `fp61` embed (low 61 bits). using xor61 = xor_wrapper; /// @brief One party's share of a degree-1 Shamir secret. struct share { /// @brief Party index in `{1, 2, 3}`. int party = 1; /// @brief The field element `s + a·party`. fp61 value{}; }; /// @brief Modular inverse in `fp61`. /// @param a a non-zero field element /// @return `a^{-1}` /// @throws std::invalid_argument if `a` is zero HEDLEY_WARN_UNUSED_RESULT inline fp61 inv(fp61 a) { return detail::shamir_field::inv(a); } /// @brief Embed a field element as a 61-bit XOR string. /// @param a the field element /// @return the low 61 bits as an XOR word HEDLEY_NO_THROW HEDLEY_CONST HEDLEY_ALWAYS_INLINE xor61 embed_xor(fp61 a) noexcept { return xor61{a.raw()}; } /// @brief Embed a 61-bit XOR string as a field element. /// @param w the XOR word (high bits ignored) /// @return `fp61` of the low 61 bits HEDLEY_NO_THROW HEDLEY_CONST HEDLEY_ALWAYS_INLINE fp61 embed_field(xor61 w) noexcept { return fp61{static_cast(w) & fp61_mod}; } /// @brief Field-to-XOR then XOR: `embed(a) ⊕ w`. /// @param a the field element /// @param w the XOR string /// @return the combined XOR string HEDLEY_NO_THROW HEDLEY_CONST HEDLEY_ALWAYS_INLINE xor61 field_xor(fp61 a, xor61 w) noexcept { return embed_xor(a) + w; } /// @brief XOR-to-field then multiply: `embed(w) · c`. /// @param w the XOR string /// @param c the field scale /// @return the product in `fp61` HEDLEY_NO_THROW HEDLEY_CONST HEDLEY_ALWAYS_INLINE fp61 xor_scale(xor61 w, fp61 c) noexcept { return embed_field(w) * c; } /// @brief Sample a degree-1 sharing of `secret` at parties 1, 2, and 3. /// @details `shamir::share_secret`, written as runtime points /// `1`, `2`, and `3`. /// @param secret the cleartext secret /// @return shares for parties 1, 2, and 3 /// \complexity O(1). One random slope and three field multiplies. No messages. HEDLEY_WARN_UNUSED_RESULT inline std::array share_secret(fp61 secret) { const auto dealt = shamir::share_secret(secret); return { share{1, std::get<0>(dealt).raw()}, share{2, std::get<1>(dealt).raw()}, share{3, std::get<2>(dealt).raw()}, }; } /// @brief Reconstruct from any two distinct party shares. /// @details `shamir::reconstruct` after the (2,3) party check. /// @param a first share /// @param b second share /// @return the secret /// @throws std::invalid_argument if the party indices collide or are out of range /// \complexity O(1). Two field inverses and a handful of multiplies. The shares are already in hand; this function does not exchange them. HEDLEY_WARN_UNUSED_RESULT inline fp61 reconstruct(share a, share b) { if (a.party == b.party || a.party < 1 || a.party > 3 || b.party < 1 || b.party > 3) throw std::invalid_argument("shamir3: need two distinct parties in 1..3"); const std::array, 2> held{{ shamir::point_share{a.party, a.value}, shamir::point_share{b.party, b.value}, }}; return shamir::reconstruct(held); } /// @brief Reconstruct from all three shares (any two suffice; this checks consistency). /// @param a party 1 share /// @param b party 2 share /// @param c party 3 share /// @return the secret /// @throws std::invalid_argument if the party indices are wrong /// @throws std::runtime_error if the three shares are inconsistent /// \complexity O(1). Two field inverses and a handful of multiplies. The shares are already in hand; this function does not exchange them. HEDLEY_WARN_UNUSED_RESULT inline fp61 reconstruct(share a, share b, share c) { const fp61 from_ab = reconstruct(a, b); const fp61 from_ac = reconstruct(a, c); if (from_ab != from_ac) throw std::runtime_error("shamir3: inconsistent shares"); return from_ab; } /// @brief Scale a share by its party index: `value · party`. /// @param s the share /// @return `s.value * s.party` HEDLEY_NO_THROW HEDLEY_CONST HEDLEY_ALWAYS_INLINE fp61 party_scale(share s) noexcept { return s.value * fp61{static_cast(s.party)}; } /// @brief Prefactor `s_i · i^{-1}` used when planting Shamir shares into VDPF+. /// @param s the share /// @return `s.value * inv(party)` HEDLEY_WARN_UNUSED_RESULT inline fp61 unscale(share s) { return s.value * inv(fp61{static_cast(s.party)}); } /// @brief Add two shares of the same party. /// @param a left share /// @param b right share /// @return the sum /// @throws std::invalid_argument if the parties differ HEDLEY_WARN_UNUSED_RESULT inline share add(share a, share b) { if (a.party != b.party) throw std::invalid_argument("shamir3: add different parties"); return share{a.party, a.value + b.value}; } /// @brief Typed share for runtime party `s.party` in `{1, 2, 3}`. /// @throws std::invalid_argument if `s.party` is outside `1..3` template HEDLEY_WARN_UNUSED_RESULT inline shamir_share typed_share(share s) { if (s.party != static_cast(Party) + 1) throw std::invalid_argument("shamir3: typed party mismatch"); return shamir_share::from_raw(s.value); } /// @brief Runtime share for a typed party `Party` (point `Party + 1`). template HEDLEY_NO_THROW HEDLEY_CONST inline share runtime_share(const shamir_share & s) noexcept { return share{static_cast(Party) + 1, s.raw()}; } } // namespace shamir3 } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_SHAMIR3_HPP__