/// @file dpf/secret_share.hpp /// @brief Thin secret-share wrappers. /// @details (2,2)-additive and (2,2)-subtractive shares, and (3,3)-additive /// shares, are layout-identical to `T`. A (2,3)-replicated share /// holds two components of a (3,3)-additive sharing: party `i` /// stores `(x_i, x_{i+1 mod 3})`. /// Reconstruction: (2,2)-additive opens by sum, (2,2)-subtractive by /// `share0 - share1`, (3,3)-additive by the sum of all three shares, /// and (2,3)-replicated from any two parties. /// Linear combinations of same-party, same-scheme shares are local. /// Mixing (2,2)-additive with (2,2)-subtractive applies the party /// coefficient. A public plaintext absorbs on party 0 for a one-word /// share, and into component `x_0` for a replicated share. /// @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_SECRET_SHARE_HPP__ #define LIBDPF_INCLUDE_DPF_SECRET_SHARE_HPP__ #include #include #include #include #include #include #include #include #include #include #include "hedley/hedley.h" #include "dpf/shamir.hpp" #include "dpf/twiddle.hpp" namespace dpf { /// @brief Sharing scheme tag. enum class sharing : unsigned char { /// (2,2) additive: `s = s0 + s1`. additive = 0, /// (2,2) subtractive: `s = s0 - s1`. subtractive = 1, /// (3,3) additive: `s = s0 + s1 + s2`. Every share is required. additive3 = 2, /// (2,3) replicated: party `i` holds `(x_i, x_{i+1 mod 3})` with /// `s = x0 + x1 + x2`. Any two parties reconstruct. replicated = 3, /// (2,2) FSS leaf share. Opens like subtractive (`s0 - s1`). The distinct /// tag is the evaluator's leaf output, not a generic subtractive word. fss = 4, /// (2,3) Shamir, `shamir::two_of_three`. Party `i` holds `s + slope·(i+1)` /// in a field. Any two parties reconstruct by Lagrange. Points are `1`, /// `2`, and `3`. This is `shamir::share`. Other thresholds /// use `shamir::share`. shamir = 5 }; template struct secret_share; template using additive_share = secret_share; template using subtractive_share = secret_share; template using additive3_share = secret_share; template using replicated_share = secret_share; template using fss_share = secret_share; template using shamir_share = secret_share; /// @brief `true` for (2,2) additive, subtractive, and FSS leaf shares. template inline constexpr bool is_two_party_sharing_v = Scheme == sharing::additive || Scheme == sharing::subtractive || Scheme == sharing::fss; /// @brief Parties who hold a share of `Scheme`. template inline constexpr std::size_t sharing_parties_v = is_two_party_sharing_v ? 2 : 3; /// @brief Shares required to open `Scheme`. template inline constexpr std::size_t sharing_threshold_v = (Scheme == sharing::replicated || Scheme == sharing::shamir) ? 2 : sharing_parties_v; template struct is_secret_share : std::false_type { }; template struct is_secret_share> : std::true_type { }; template inline constexpr bool is_secret_share_v = is_secret_share>::value; template struct share_party; template struct share_party> : std::integral_constant { }; template inline constexpr std::size_t share_party_v = share_party>::value; template struct share_scheme; template struct share_scheme> : std::integral_constant { }; template inline constexpr sharing share_scheme_v = share_scheme>::value; template struct share_value_type; template struct share_value_type> { using type = T; }; template using share_value_type_t = typename share_value_type>::type; namespace detail { /// @brief Two's-complement negation. Signed minimum stays defined. /// @tparam T value type /// @param v the `v` /// @return the group negation used by a (2,2) sign flip template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T negate_word(const T & v) noexcept { if constexpr (std::is_integral_v && std::is_signed_v) { using unsigned_type = std::make_unsigned_t; return static_cast(static_cast(0) - static_cast(v)); } else return static_cast(-v); } /// @brief Group sum. IEEE `float` / `double` add by XOR of the bits, matching /// the leaf group. Signed integers add in the unsigned width. /// @tparam T value type /// @param a left addend /// @param b right addend /// @return `a` plus `b` in the share group template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T group_add(const T & a, const T & b) noexcept { if constexpr (std::is_same_v || std::is_same_v) { using bits_t = std::conditional_t; bits_t xa{}, xb{}; std::memcpy(&xa, std::addressof(a), sizeof(T)); std::memcpy(&xb, std::addressof(b), sizeof(T)); bits_t xc = static_cast(xa ^ xb); T out{}; std::memcpy(&out, &xc, sizeof(T)); return out; } else if constexpr (std::is_integral_v && std::is_signed_v) { using unsigned_type = std::make_unsigned_t; return static_cast(static_cast(a) + static_cast(b)); } else return static_cast(a + b); } /// @brief Group difference. IEEE bits subtract by XOR. Signed integers /// subtract in the unsigned width. /// @tparam T value type /// @param a minuend /// @param b subtrahend /// @return `a` minus `b` in the share group template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T group_sub(const T & a, const T & b) noexcept { if constexpr (std::is_same_v || std::is_same_v) return group_add(a, b); else if constexpr (std::is_integral_v && std::is_signed_v) { using unsigned_type = std::make_unsigned_t; return static_cast(static_cast(a) - static_cast(b)); } else return static_cast(a - b); } /// @brief Ring product. IEEE bits use AND, matching `leaf_group_mul`. Signed /// integers multiply in the unsigned width. /// @tparam T value type /// @param a left factor /// @param b right factor /// @return `a` times `b` in the share ring template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T group_mul(const T & a, const T & b) noexcept { if constexpr (std::is_same_v || std::is_same_v) { using bits_t = std::conditional_t; bits_t xa{}, xb{}; std::memcpy(&xa, std::addressof(a), sizeof(T)); std::memcpy(&xb, std::addressof(b), sizeof(T)); bits_t xc = static_cast(xa & xb); T out{}; std::memcpy(&out, &xc, sizeof(T)); return out; } else if constexpr (std::is_integral_v && std::is_signed_v) { using unsigned_type = std::make_unsigned_t; return static_cast(static_cast(a) * static_cast(b)); } else return static_cast(a * b); } /// @brief Sign of a (2,2) party's word. Additive is always +1. Subtractive /// and FSS are +1 on party 0 and −1 on party 1. /// @tparam Scheme scheme /// @tparam Party party index template inline constexpr int two_party_sign_v = (Party == 0 || Scheme == sharing::additive) ? 1 : -1; /// @brief Rewrite a (2,2) word from `From` into `To`. Negate iff the signs differ. /// @tparam From source scheme /// @tparam To destination scheme /// @tparam Party party index /// @tparam T value type /// @param v the stored word /// @return the word in `To` template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T retarget_word(const T & v) noexcept { if constexpr (two_party_sign_v == two_party_sign_v) return v; else return negate_word(v); } // `detail::shamir_field` is the field inverse for Shamir reconstruction. // The primary template lives in `shamir.hpp`. `fp61` and `gf2n` specialize it. } // namespace detail template struct secret_share { static_assert( (is_two_party_sharing_v && (Party == 0 || Party == 1)) || (Scheme == sharing::additive3 && Party < 3), "secret_share: (2,2) parties are 0 or 1; " "(3,3)-additive parties are 0, 1, or 2"); using value_type = T; static constexpr std::size_t party = Party; static constexpr sharing scheme = Scheme; T value{}; secret_share() = default; HEDLEY_NO_THROW secret_share(const secret_share &) noexcept = default; HEDLEY_NO_THROW secret_share(secret_share &&) noexcept = default; HEDLEY_NO_THROW secret_share & operator=(const secret_share &) noexcept = default; HEDLEY_NO_THROW secret_share & operator=(secret_share &&) noexcept = default; ~secret_share() = default; /// @brief Bit-preserving construction. Does not apply a party coefficient. /// @param v the `v` /// @return Bit-preserving construction HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST static constexpr secret_share from_raw(T v) noexcept { secret_share s; s.value = v; return s; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST constexpr const T & raw() const noexcept { return value; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_PURE constexpr T & raw() noexcept { return value; } /// @brief Secret-preserving conversion to an additive share of the same party. /// @return Secret-preserving conversion to an additive share of the same party HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_PURE constexpr additive_share as_additive() const noexcept { static_assert(is_two_party_sharing_v, "as_additive converts a (2,2) share; " "a (3,3) component is already additive, and a replicated share " "uses as_additive3()"); if constexpr (Scheme == sharing::additive) return additive_share::from_raw(value); // Subtractive and FSS: party 0 keeps bits; party 1 negates. return additive_share::from_raw( detail::retarget_word(value)); } /// @brief Secret-preserving conversion to a subtractive share of the same party. /// @return Secret-preserving conversion to a subtractive share of the same party HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_PURE constexpr subtractive_share as_subtractive() const noexcept { static_assert(is_two_party_sharing_v, "as_subtractive converts a (2,2) share"); if constexpr (Scheme == sharing::subtractive) return subtractive_share::from_raw(value); // Additive party 1 negates. FSS already opens like subtractive. return subtractive_share::from_raw( detail::retarget_word(value)); } /// @brief Secret-preserving conversion to an FSS leaf share of the same party. /// @details FSS leaves open like subtractive shares. Additive party 1 negates. /// @return the FSS share HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_PURE constexpr fss_share as_fss() const noexcept { static_assert(is_two_party_sharing_v, "as_fss converts a (2,2) share"); if constexpr (Scheme == sharing::fss) return fss_share::from_raw(value); return fss_share::from_raw( detail::retarget_word(value)); } /// @brief Bit-preserving retag (no secret-preserving sign fix). /// @tparam NewScheme new scheme /// @tparam NewParty new party /// @return Bit-preserving retag (no secret-preserving sign fix) template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share retag() const noexcept { return secret_share::from_raw(value); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_PURE constexpr secret_share operator-() const noexcept { return from_raw(detail::negate_word(value)); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr secret_share & operator+=(const secret_share & rhs) noexcept { value = static_cast(value + rhs.value); return *this; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr secret_share & operator-=(const secret_share & rhs) noexcept { value = static_cast(value - rhs.value); return *this; } template , int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr secret_share & operator*=(const Scalar & c) noexcept { value = static_cast(value * static_cast(c)); return *this; } /// @brief Absorb a public plaintext on party 0 only. /// @tparam Plain plain /// @tparam T value type /// @param c the `c` /// @return `*this` template && std::is_convertible_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr secret_share & operator+=(const Plain & c) noexcept { if constexpr (Party == 0) value = static_cast(value + static_cast(c)); return *this; } template && std::is_convertible_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr secret_share & operator-=(const Plain & c) noexcept { if constexpr (Party == 0) value = static_cast(value - static_cast(c)); return *this; } }; /// @brief (2,3) replicated share. Party `Party` holds `(x_Party, x_{Party+1})`. /// @details `own` is this party's (3,3) component. `next` is the following /// party's component, stored here so any two parties hold every /// component. Local `+`, `-`, and scalar `*` touch both words. /// A public plaintext is added only to `x_0`: party 0 updates `own`, /// party 2 updates `next`, party 1 is unchanged. /// @tparam T value type /// @tparam Party party index, `0`, `1`, or `2` template struct secret_share { static_assert(Party < 3, "replicated_share party must be 0, 1, or 2"); using value_type = T; static constexpr std::size_t party = Party; static constexpr sharing scheme = sharing::replicated; static constexpr std::size_t next_party = (Party + 1) % 3; /// Component `x_Party`. T own{}; /// Component `x_{Party+1 mod 3}`. T next{}; secret_share() = default; HEDLEY_NO_THROW secret_share(const secret_share &) noexcept = default; HEDLEY_NO_THROW secret_share(secret_share &&) noexcept = default; HEDLEY_NO_THROW secret_share & operator=(const secret_share &) noexcept = default; HEDLEY_NO_THROW secret_share & operator=(secret_share &&) noexcept = default; ~secret_share() = default; /// @brief Bit-preserving construction. /// @param own_v component `x_Party` /// @param next_v component `x_{Party+1 mod 3}` /// @return the share HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST static constexpr secret_share from_raw(T own_v, T next_v) noexcept { secret_share s; s.own = own_v; s.next = next_v; return s; } /// @brief Build from this party's (3,3) component and the next party's. /// @param mine `x_Party` /// @param nxt `x_{Party+1 mod 3}` /// @return the replicated share HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST static constexpr secret_share from_additive3( const additive3_share & mine, const additive3_share & nxt) noexcept { return from_raw(mine.raw(), nxt.raw()); } /// @brief This party's underlying (3,3) component (`own`). /// @return an `additive3_share` of the same party HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_PURE constexpr additive3_share as_additive3() const noexcept { return additive3_share::from_raw(own); } /// @brief The next party's (3,3) component, as stored in `next`. /// @return an `additive3_share` of party `Party + 1 mod 3` HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_PURE constexpr additive3_share next_additive3() const noexcept { return additive3_share::from_raw(next); } /// @brief Add `v` into `x_Party`. /// @details The previous party stores the same component as `next` and /// must apply the same addend, or the two replicas diverge. /// `add_replicated` updates both holders. /// @param v addend in the share group /// @return `*this` HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr secret_share & add_own(const T & v) noexcept { own = detail::group_add(own, v); return *this; } /// @brief Add `v` into `x_{Party+1}`. /// @details Party `Party+1` stores that component as `own` and must apply /// the same addend. /// @param v addend in the share group /// @return `*this` HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr secret_share & add_next(const T & v) noexcept { next = detail::group_add(next, v); return *this; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr secret_share operator-() const noexcept { return from_raw(detail::negate_word(own), detail::negate_word(next)); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr secret_share & operator+=(const secret_share & rhs) noexcept { own = detail::group_add(own, rhs.own); next = detail::group_add(next, rhs.next); return *this; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr secret_share & operator-=(const secret_share & rhs) noexcept { own = detail::group_sub(own, rhs.own); next = detail::group_sub(next, rhs.next); return *this; } template , int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr secret_share & operator*=(const Scalar & c) noexcept { own = static_cast(own * static_cast(c)); next = static_cast(next * static_cast(c)); return *this; } /// @brief Absorb a public plaintext into `x_0` only. /// @tparam Plain plain /// @param c the `c` /// @return `*this` template && std::is_convertible_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr secret_share & operator+=(const Plain & c) noexcept { const T addend = static_cast(c); if constexpr (Party == 0) own = detail::group_add(own, addend); else if constexpr (Party == 2) next = detail::group_add(next, addend); return *this; } template && std::is_convertible_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr secret_share & operator-=(const Plain & c) noexcept { const T subtrahend = static_cast(c); if constexpr (Party == 0) own = detail::group_sub(own, subtrahend); else if constexpr (Party == 2) next = detail::group_sub(next, subtrahend); return *this; } }; /// @brief (2,3) Shamir share. Party `Party` holds `s + slope·(Party+1)`. /// @details This is `shamir::share`, the `shamir::two_of_three` /// case of `(K,N)` Shamir. The evaluation points are `1`, `2`, and /// `3`, matching `shamir3::share`. A public plaintext is added on /// every party, because every evaluation of `s + c` grows by `c`. /// Scalar multiplication scales the share. Two shares multiply to a /// degree-2 polynomial, which is not a Shamir share; use `rss_mul` /// for a (2,3) product. /// @tparam T field element /// @tparam Party party index, `0`, `1`, or `2` template struct secret_share { static_assert(Party < 3, "shamir_share party must be 0, 1, or 2"); using value_type = T; using access_type = shamir::two_of_three; static constexpr std::size_t party = Party; static constexpr sharing scheme = sharing::shamir; static constexpr std::size_t threshold = access_type::threshold; static constexpr std::size_t parties = access_type::parties; static constexpr std::size_t degree = access_type::degree; /// Lagrange point. Party 0 is point 1. static constexpr std::uint64_t point = Party + 1; T value{}; secret_share() = default; HEDLEY_NO_THROW secret_share(const secret_share &) noexcept = default; HEDLEY_NO_THROW secret_share(secret_share &&) noexcept = default; HEDLEY_NO_THROW secret_share & operator=(const secret_share &) noexcept = default; HEDLEY_NO_THROW secret_share & operator=(secret_share &&) noexcept = default; ~secret_share() = default; /// @brief Bit-preserving construction. Does not apply a Lagrange weight. /// @param v the field element /// @return the share HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST static constexpr secret_share from_raw(T v) noexcept { secret_share s; s.value = v; return s; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST constexpr const T & raw() const noexcept { return value; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_PURE constexpr T & raw() noexcept { return value; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr secret_share operator-() const noexcept { return from_raw(static_cast(-value)); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr secret_share & operator+=(const secret_share & rhs) noexcept { value = static_cast(value + rhs.value); return *this; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr secret_share & operator-=(const secret_share & rhs) noexcept { value = static_cast(value - rhs.value); return *this; } template , int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr secret_share & operator*=(const Scalar & c) noexcept { value = static_cast(value * static_cast(c)); return *this; } /// @brief Absorb a public plaintext on every party. /// @tparam Plain plain /// @param c the `c` /// @return `*this` template && std::is_convertible_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr secret_share & operator+=(const Plain & c) noexcept { value = static_cast(value + static_cast(c)); return *this; } template && std::is_convertible_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr secret_share & operator-=(const Plain & c) noexcept { value = static_cast(value - static_cast(c)); return *this; } }; namespace shamir { /// @brief `(2,3)` is the `sharing::shamir` share, not `basic_share`. template struct share_of { using type = secret_share; }; template struct params> : std::true_type { using value_type = T; static constexpr std::size_t party = Party; static constexpr std::size_t threshold = 2; static constexpr std::size_t parties = 3; static constexpr std::uint64_t point = secret_share::point; }; } // namespace shamir // --------------------------------------------------------------------------- // Same-scheme, same-party arithmetic // --------------------------------------------------------------------------- template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator+( secret_share lhs, const secret_share & rhs) noexcept { lhs += rhs; return lhs; } template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator-( secret_share lhs, const secret_share & rhs) noexcept { lhs -= rhs; return lhs; } template , int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator*( secret_share lhs, const Scalar & c) noexcept { lhs *= c; return lhs; } template , int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator*( const Scalar & c, secret_share rhs) noexcept { rhs *= c; return rhs; } // --------------------------------------------------------------------------- // Cross-scheme, same-party (2,2) only. Keep the left-hand scheme. Party 1 // flips the right-hand word when the two schemes disagree on its sign. // Subtractive and FSS share a sign, so mixing those does not flip. // --------------------------------------------------------------------------- template && is_two_party_sharing_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator+( const secret_share & lhs, const secret_share & rhs) noexcept { if constexpr (detail::two_party_sign_v == detail::two_party_sign_v) return secret_share::from_raw( static_cast(lhs.raw() + rhs.raw())); else return secret_share::from_raw( static_cast(lhs.raw() - rhs.raw())); } template && is_two_party_sharing_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator-( const secret_share & lhs, const secret_share & rhs) noexcept { if constexpr (detail::two_party_sign_v == detail::two_party_sign_v) return secret_share::from_raw( static_cast(lhs.raw() - rhs.raw())); else return secret_share::from_raw( static_cast(lhs.raw() + rhs.raw())); } // --------------------------------------------------------------------------- // Plaintext absorb (party 0 only) // --------------------------------------------------------------------------- template && std::is_convertible_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator+( secret_share lhs, const Plain & c) noexcept { lhs += c; return lhs; } template && std::is_convertible_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator+( const Plain & c, secret_share rhs) noexcept { rhs += c; return rhs; } template && std::is_convertible_v, int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator-( secret_share lhs, const Plain & c) noexcept { lhs -= c; return lhs; } // --------------------------------------------------------------------------- // Equality (same party, same scheme) — compare raw bits // --------------------------------------------------------------------------- template = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr bool operator==(const secret_share & lhs, const secret_share & rhs) noexcept { return lhs.raw() == rhs.raw(); } template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr bool operator==(const replicated_share & lhs, const replicated_share & rhs) noexcept { return lhs.own == rhs.own && lhs.next == rhs.next; } template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr bool operator!=(const secret_share & lhs, const secret_share & rhs) noexcept { return !(lhs == rhs); } // --------------------------------------------------------------------------- // Reconstruction // --------------------------------------------------------------------------- template , int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T reconstruct(const secret_share & s0, const secret_share & s1) noexcept { if constexpr (Scheme == sharing::additive) return detail::group_add(s0.raw(), s1.raw()); else return detail::group_sub(s0.raw(), s1.raw()); } template , int> = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T reconstruct(const secret_share & s1, const secret_share & s0) noexcept { return reconstruct(s0, s1); } namespace detail { template HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr void store_additive3(T & c0, T & c1, T & c2, const additive3_share & share) noexcept { if constexpr (Party == 0) c0 = share.raw(); else if constexpr (Party == 1) c1 = share.raw(); else c2 = share.raw(); } template HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW constexpr void store_replicated_own(T & c0, T & c1, T & c2, const replicated_share & share) noexcept { if constexpr (Party == 0) c0 = share.own; else if constexpr (Party == 1) c1 = share.own; else c2 = share.own; } } // namespace detail /// @brief Open a (3,3)-additive sharing. Parties may be passed in any order. /// @tparam T value type /// @tparam P party of the first share /// @tparam Q party of the second share /// @tparam R party of the third share /// @param a first share /// @param b second share /// @param c third share /// @return `x0 + x1 + x2` template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T reconstruct(const additive3_share & a, const additive3_share & b, const additive3_share & c) noexcept { static_assert(P != Q && Q != R && P != R, "additive3 reconstruct: need three distinct parties"); T c0{}, c1{}, c2{}; detail::store_additive3(c0, c1, c2, a); detail::store_additive3(c0, c1, c2, b); detail::store_additive3(c0, c1, c2, c); return detail::group_add(detail::group_add(c0, c1), c2); } /// @brief Open a (2,3)-replicated sharing from any two parties. /// @details Party `P` contributes `(x_P, x_{P+1})`. The missing component is /// taken from party `Q`. /// @tparam T value type /// @tparam P first party /// @tparam Q second party /// @param a first share /// @param b second share /// @return `x0 + x1 + x2` template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T reconstruct(const replicated_share & a, const replicated_share & b) noexcept { static_assert(P < 3 && Q < 3 && P != Q, "replicated reconstruct: need two distinct parties in 0..2"); constexpr std::size_t missing = (P + 2) % 3; T third{}; if constexpr (Q == missing) third = b.own; else third = b.next; return detail::group_add(detail::group_add(a.own, a.next), third); } /// @brief Open a (2,3)-replicated sharing from all three `own` components. /// @details This is the underlying (3,3) sum. `next` is not read. /// @tparam T value type /// @tparam P first party /// @tparam Q second party /// @tparam R third party /// @param a first share /// @param b second share /// @param c third share /// @return `x0 + x1 + x2` template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T reconstruct(const replicated_share & a, const replicated_share & b, const replicated_share & c) noexcept { static_assert(P != Q && Q != R && P != R, "replicated reconstruct: need three distinct parties"); T c0{}, c1{}, c2{}; detail::store_replicated_own(c0, c1, c2, a); detail::store_replicated_own(c0, c1, c2, b); detail::store_replicated_own(c0, c1, c2, c); return detail::group_add(detail::group_add(c0, c1), c2); } /// @brief Open a (2,3) Shamir sharing from any two parties. /// @details `shamir::reconstruct` for `shamir::two_of_three`. /// @tparam T field type. Requires `detail::shamir_field` /// @tparam P first party /// @tparam Q second party /// @param a first share /// @param b second share /// @return the secret /// @throws std::invalid_argument if a Lagrange denominator is zero template HEDLEY_WARN_UNUSED_RESULT T reconstruct(const shamir_share & a, const shamir_share & b) { return shamir::reconstruct(a, b); } /// @brief Open a (2,3) Shamir sharing from all three parties. /// @details The third share is checked against the polynomial of the first two. /// @throws std::invalid_argument if a party index is repeated /// @throws std::runtime_error if the three shares are inconsistent template HEDLEY_WARN_UNUSED_RESULT T reconstruct(const shamir_share & a, const shamir_share & b, const shamir_share & c) { return shamir::reconstruct(a, b, c); } // --------------------------------------------------------------------------- // Plaintext splits (share1 = 0) // --------------------------------------------------------------------------- template HEDLEY_ALWAYS_INLINE HEDLEY_CONST HEDLEY_NO_THROW constexpr auto make_additive_shares(T secret) noexcept { using T_ = std::remove_cv_t>; return std::make_pair( additive_share::from_raw(static_cast(secret)), additive_share::from_raw(T_{})); } template HEDLEY_ALWAYS_INLINE HEDLEY_CONST HEDLEY_NO_THROW constexpr auto make_subtractive_shares(T secret) noexcept { using T_ = std::remove_cv_t>; return std::make_pair( subtractive_share::from_raw(static_cast(secret)), subtractive_share::from_raw(T_{})); } template HEDLEY_ALWAYS_INLINE HEDLEY_CONST HEDLEY_NO_THROW constexpr auto make_additive3_shares(T secret) noexcept { using T_ = std::remove_cv_t>; return std::make_tuple( additive3_share::from_raw(static_cast(secret)), additive3_share::from_raw(T_{}), additive3_share::from_raw(T_{})); } /// @brief Shamir shares of `secret` for access structure `(K,N)`. /// @details Party `i` stores `p(i+1)` where /// `p(x) = secret + coeff[0] x + ... + coeff[K-2] x^{K-1}`. /// This is `shamir::deal`. /// @tparam K reconstruction threshold /// @tparam N shareholder count /// @tparam T field type /// @param secret the constant term /// @param coeff higher coefficients, low degree first /// @return shares for parties `0 .. N-1` template HEDLEY_ALWAYS_INLINE HEDLEY_CONST HEDLEY_NO_THROW constexpr auto make_shamir_shares(T secret, const std::array>, shamir::access::degree> & coeff) noexcept { using T_ = std::remove_cv_t>; return shamir::deal(static_cast(secret), coeff); } /// @brief Degree-1 Shamir shares of `secret` with the given slope. /// @details `(K,N) = (2,3)`. Party `i` stores `secret + slope·(i+1)`. /// This is `make_shamir_shares<2, 3>` with that one coefficient. /// @tparam T field type /// @param secret the cleartext secret /// @param slope the uniform slope. Zero puts `secret` on every party /// @return shares for parties 0, 1, and 2 template HEDLEY_ALWAYS_INLINE HEDLEY_CONST HEDLEY_NO_THROW constexpr auto make_shamir_shares(T secret, T slope) noexcept { using T_ = std::remove_cv_t>; return make_shamir_shares<2, 3>(static_cast(secret), std::array{{static_cast(slope)}}); } /// @brief Deterministic (2,3) replicated split. The secret sits in `x_0`. /// @details Party 0 stores `(secret, 0)`, party 1 stores `(0, 0)`, party 2 /// stores `(0, secret)`. /// @tparam T value type /// @param secret the cleartext secret /// @return shares for parties 0, 1, and 2 template HEDLEY_ALWAYS_INLINE HEDLEY_CONST HEDLEY_NO_THROW constexpr auto make_replicated_shares(T secret) noexcept { using T_ = std::remove_cv_t>; const T_ s = static_cast(secret); const T_ z{}; return std::make_tuple( replicated_share::from_raw(s, z), replicated_share::from_raw(z, z), replicated_share::from_raw(z, s)); } /// @brief Replicated shares of the (3,3) components `x0`, `x1`, and `x2`. /// @tparam T value type /// @param x0 component held by parties 0 and 2 /// @param x1 component held by parties 0 and 1 /// @param x2 component held by parties 1 and 2 /// @return shares for parties 0, 1, and 2 template HEDLEY_ALWAYS_INLINE HEDLEY_CONST HEDLEY_NO_THROW constexpr auto make_replicated_shares(T x0, T x1, T x2) noexcept { using T_ = std::remove_cv_t>; const T_ a = static_cast(x0); const T_ b = static_cast(x1); const T_ c = static_cast(x2); return std::make_tuple( replicated_share::from_raw(a, b), replicated_share::from_raw(b, c), replicated_share::from_raw(c, a)); } /// @brief Replicated shares of an existing (3,3)-additive sharing. /// @tparam T value type /// @param s0 party 0's component /// @param s1 party 1's component /// @param s2 party 2's component /// @return shares for parties 0, 1, and 2 template HEDLEY_ALWAYS_INLINE HEDLEY_CONST HEDLEY_NO_THROW constexpr auto make_replicated_shares(const additive3_share & s0, const additive3_share & s1, const additive3_share & s2) noexcept { return make_replicated_shares(s0.raw(), s1.raw(), s2.raw()); } /// @brief Fold a (3,3)-additive sharing into a replicated sharing. /// @details Each component is added at both parties that store it, so the /// replicas stay equal. /// @tparam T value type /// @param r0 party 0 /// @param r1 party 1 /// @param r2 party 2 /// @param a0 addend component 0 /// @param a1 addend component 1 /// @param a2 addend component 2 /// @return the updated replicated shares template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr auto add_replicated( replicated_share r0, replicated_share r1, replicated_share r2, const additive3_share & a0, const additive3_share & a1, const additive3_share & a2) noexcept { r0.add_own(a0.raw()); r0.add_next(a1.raw()); r1.add_own(a1.raw()); r1.add_next(a2.raw()); r2.add_own(a2.raw()); r2.add_next(a0.raw()); return std::make_tuple(r0, r1, r2); } // --------------------------------------------------------------------------- // Share conversions // // Letters: a (2,2) additive, b (2,2) subtractive, fss (2,2) leaf share, // y (3,3) additive, rss (2,3) replicated, s (2,3) Shamir. // // Local, one party, secret-preserving: // a2b b2a a2fss fss2a b2fss fss2b // rss2y (this party's component) // y2rss(own, next) (one replicated share) // Local, a reconstructing set in one place (the secret is opened, then split): // y2rss(y0,y1,y2) rss2y(r0,r1,r2) // s2y y2s s2rss rss2s // Not local, and not defined here: a2y y2a b2y y2b a2rss rss2a b2rss rss2b // fss2y y2fss fss2rss rss2fss, and Shamir with a single (2,2) share. // Those change the party count. The garbled-bit conversions with the same // names (a2y through y2rss) are dpf::yao in dpf/yao_share.hpp. They are not // these casts. Top-level rss2y / y2rss stay the local (3,3) operations. // --------------------------------------------------------------------------- /// @brief (2,2) additive to subtractive. Party 1 negates. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr subtractive_share a2b(const additive_share & s) noexcept { return s.as_subtractive(); } /// @brief (2,2) subtractive to additive. Party 1 negates. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr additive_share b2a(const subtractive_share & s) noexcept { return s.as_additive(); } /// @brief (2,2) additive to an FSS leaf share. Party 1 negates. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr fss_share a2fss(const additive_share & s) noexcept { return s.as_fss(); } /// @brief FSS leaf share to (2,2) additive. Party 1 negates. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr additive_share fss2a(const fss_share & s) noexcept { return s.as_additive(); } /// @brief (2,2) subtractive to an FSS leaf share. Same opening, bits unchanged. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr fss_share b2fss(const subtractive_share & s) noexcept { return s.as_fss(); } /// @brief FSS leaf share to (2,2) subtractive. Same opening, bits unchanged. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr subtractive_share fss2b(const fss_share & s) noexcept { return s.as_subtractive(); } /// @brief This party's (3,3) component of a replicated share (`y` = additive3). template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr additive3_share rss2y(const replicated_share & s) noexcept { return s.as_additive3(); } /// @brief One replicated share from this party's component and the next party's. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr replicated_share y2rss( const additive3_share & mine, const additive3_share::next_party> & nxt) noexcept { return replicated_share::from_additive3(mine, nxt); } /// @brief Replicated shares of a (3,3)-additive sharing. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr auto y2rss(const additive3_share & y0, const additive3_share & y1, const additive3_share & y2) noexcept { return make_replicated_shares(y0, y1, y2); } /// @brief The three (3,3) components of a replicated sharing. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr auto rss2y(const replicated_share & r0, const replicated_share & r1, const replicated_share & r2) noexcept { return std::make_tuple(r0.as_additive3(), r1.as_additive3(), r2.as_additive3()); } /// @brief Open two Shamir shares and split the secret as (3,3) additive. /// @details The secret sits on party 0. The other two components are zero. template HEDLEY_WARN_UNUSED_RESULT auto s2y(const shamir_share & a, const shamir_share & b) { return make_additive3_shares(reconstruct(a, b)); } /// @brief Shamir-share a (3,3) additive secret with `slope`. template HEDLEY_WARN_UNUSED_RESULT auto y2s(const additive3_share & y0, const additive3_share & y1, const additive3_share & y2, T slope) { return make_shamir_shares(reconstruct(y0, y1, y2), slope); } /// @brief Open two Shamir shares and split the secret as replicated shares. /// @details Component `x_0` holds the secret. The other components are zero. template HEDLEY_WARN_UNUSED_RESULT auto s2rss(const shamir_share & a, const shamir_share & b) { return make_replicated_shares(reconstruct(a, b)); } /// @brief Shamir-share a replicated secret with `slope`. template HEDLEY_WARN_UNUSED_RESULT auto rss2s(const replicated_share & a, const replicated_share & b, T slope) { return make_shamir_shares(reconstruct(a, b), slope); } /// @brief Local factor of an RSS product: `x_i y_i + x_i y_{i+1} + x_{i+1} y_i`. /// @details The three parties' terms sum to the product. Party `i+1`'s term is /// not known here; `rss_mul` on all three shares replicates it. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr additive3_share rss_mul( const replicated_share & x, const replicated_share & y) noexcept { const T term = detail::group_add( detail::group_add(detail::group_mul(x.own, y.own), detail::group_mul(x.own, y.next)), detail::group_mul(x.next, y.own)); return additive3_share::from_raw(term); } /// @brief RSS product once every party's local factor is in hand. /// @details Correct, and not randomized: each factor is a function of that /// party's input shares. Pass a zero (3,3) sharing to mask it. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr auto rss_mul( const replicated_share & x0, const replicated_share & x1, const replicated_share & x2, const replicated_share & y0, const replicated_share & y1, const replicated_share & y2) noexcept { return make_replicated_shares(rss_mul(x0, y0), rss_mul(x1, y1), rss_mul(x2, y2)); } /// @brief Masked RSS product. `m0 + m1 + m2` must be 0. template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr auto rss_mul( const replicated_share & x0, const replicated_share & x1, const replicated_share & x2, const replicated_share & y0, const replicated_share & y1, const replicated_share & y2, const additive3_share & m0, const additive3_share & m1, const additive3_share & m2) noexcept { return make_replicated_shares( additive3_share::from_raw( detail::group_add(rss_mul(x0, y0).raw(), m0.raw())), additive3_share::from_raw( detail::group_add(rss_mul(x1, y1).raw(), m1.raw())), additive3_share::from_raw( detail::group_add(rss_mul(x2, y2).raw(), m2.raw()))); } // --------------------------------------------------------------------------- // Party-tagged DPF key wrapper // --------------------------------------------------------------------------- template struct is_party_key : std::false_type { }; template struct party_key : Key { static_assert(Party == 0 || Party == 1, "party_key party must be 0 or 1"); static constexpr std::size_t party = Party; using key_type = Key; party_key() = default; HEDLEY_ALWAYS_INLINE explicit party_key(Key k) : Key(std::move(k)) { #ifndef NDEBUG assert(static_cast( static_cast(dpf::get_lo_bit(this->root()))) == Party); #endif } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE Key & key() noexcept { return static_cast(*this); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE const Key & key() const noexcept { return static_cast(*this); } /// @brief Party-tagged additive share of the comparison absorb addend. /// @return Party-tagged additive share of the comparison absorb addend HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE auto cmp_addend() const noexcept { return additive_share::from_raw( Key::cmp_addend()); } }; template struct is_party_key> : std::true_type { }; template inline constexpr bool is_party_key_v = is_party_key>::value; template struct party_of; // incomplete for non-`party_key` (fail loudly on misuse) template struct party_of> : std::integral_constant { }; template inline constexpr std::size_t party_of_v = party_of>::value; /// @brief Strip a `party_key` wrapper; bare keys are unchanged. Memoizers and other /// tree-layout helpers key on the underlying DPF key type so a memoizer built /// for party 0 also accepts party 1. /// @tparam T value type template struct unwrap_party_key { using type = std::decay_t; }; template struct unwrap_party_key> { using type = Key; }; template using unwrap_party_key_t = typename unwrap_party_key>::type; template HEDLEY_ALWAYS_INLINE auto make_party_key(Key && k) { return party_key>(std::forward(k)); } template HEDLEY_ALWAYS_INLINE auto make_party_key_pair(Key0 && k0, Key1 && k1) { using K = std::decay_t; static_assert(std::is_same_v>, "make_party_key_pair: both keys must have the same type"); return std::make_pair( party_key<0, K>(std::forward(k0)), party_key<1, K>(std::forward(k1))); } template = 0> std::basic_ostream & operator<<( std::basic_ostream & os, const secret_share & s) { return os << s.raw(); } template std::basic_ostream & operator<<( std::basic_ostream & os, const replicated_share & s) { return os << '(' << s.own << ", " << s.next << ')'; } /// @brief Copy `val` into `slot`. /// @details One-word shares copy `raw()`. Replicated shares copy both /// components. A plaintext overwrites a one-word share and is not a /// replicated share. /// @tparam Slot destination slot /// @tparam Val source value /// @param slot the `slot` /// @param val the `val` template HEDLEY_ALWAYS_INLINE constexpr void assign_share_slot(Slot && slot, Val && val) { using slot_t = std::decay_t; using val_t = std::decay_t; if constexpr (is_secret_share_v) { if constexpr (is_secret_share_v) { if constexpr (share_scheme_v == sharing::replicated) { static_assert(share_scheme_v == sharing::replicated, "assign_share_slot: replicated slot needs a replicated share"); static_assert(share_party_v == share_party_v, "assign_share_slot: replicated parties differ"); slot = slot_t::from_raw(val.own, val.next); } else { static_assert(share_scheme_v != sharing::replicated, "assign_share_slot: a replicated share has two components"); slot = slot_t::from_raw(val.raw()); } } else { static_assert(share_scheme_v != sharing::replicated, "assign_share_slot: a plaintext is not a replicated share"); slot = slot_t::from_raw( static_cast(val)); } } else if constexpr (is_secret_share_v) { static_assert(share_scheme_v != sharing::replicated, "assign_share_slot: open a replicated share, or copy own and next"); slot = val.raw(); } else slot = std::forward(val); } } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_SECRET_SHARE_HPP__