/// @file dpf/secret_share.hpp /// @brief Thin (2,2) additive and subtractive secret-share wrappers. /// @details Layout-identical to `T`. Party is a compile-time `0` or `1`. /// Reconstruction: additive opens by sum, subtractive by /// `share0 - share1`. Linear combinations of same-party shares are /// supported; mixing additive with subtractive applies the correct /// party coefficient. A plaintext absorbs on party 0 only. /// @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 "hedley/hedley.h" #include "dpf/twiddle.hpp" namespace dpf { /// @brief Sharing scheme tag. enum class sharing : unsigned char { additive = 0, subtractive = 1 }; template struct secret_share; template using additive_share = secret_share; template using subtractive_share = secret_share; 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 Party coefficient of the secret for this scheme: additive always +1; /// subtractive is +1 for party 0 and −1 for party 1. /// @tparam Scheme scheme /// @tparam Party party index, `0` or `1` /// @tparam T value type /// @param v the `v` /// @return Party coefficient of the secret for this scheme: additive always +1; subtractive is +1 /// for party 0 and −1 for party 1 template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T party_coeff_times(const T & v) noexcept { if constexpr (Scheme == sharing::additive || Party == 0) return v; else if constexpr (std::is_integral_v && std::is_signed_v) { // Signed negation of the minimum is undefined. The two's-complement // negation is well-defined on the unsigned width. using unsigned_type = std::make_unsigned_t; return static_cast(static_cast(0) - static_cast(v)); } else return static_cast(-v); } } // namespace detail template struct secret_share { static_assert(Party == 0 || Party == 1, "secret_share party must be 0 or 1"); 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 { if constexpr (Scheme == sharing::additive) return additive_share::from_raw(value); // subtractive → additive: party 0 keeps bits; party 1 negates. return additive_share::from_raw( detail::party_coeff_times(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 { if constexpr (Scheme == sharing::subtractive) return subtractive_share::from_raw(value); // additive → subtractive: party 0 keeps bits; party 1 negates. return subtractive_share::from_raw( detail::party_coeff_times(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 { if constexpr (std::is_integral_v && std::is_signed_v) { using unsigned_type = std::make_unsigned_t; return from_raw(static_cast(static_cast(0) - static_cast(value))); } else 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 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; } }; // --------------------------------------------------------------------------- // 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: keep the left-hand scheme; party 1 flips the // operand whose scheme differs from the result. // --------------------------------------------------------------------------- template = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator+( const secret_share & lhs, const secret_share & rhs) noexcept { if constexpr (Party == 0) return secret_share::from_raw( static_cast(lhs.raw() + rhs.raw())); else return secret_share::from_raw( static_cast(lhs.raw() - rhs.raw())); } template = 0> HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr secret_share operator-( const secret_share & lhs, const secret_share & rhs) noexcept { if constexpr (Party == 0) 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 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 secret_share & lhs, const secret_share & rhs) noexcept { return !(lhs == rhs); } // --------------------------------------------------------------------------- // Reconstruction // --------------------------------------------------------------------------- template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T reconstruct(const secret_share & s0, const secret_share & s1) noexcept { if constexpr (std::is_integral_v && std::is_signed_v) { using unsigned_type = std::make_unsigned_t; if constexpr (Scheme == sharing::additive) return static_cast(static_cast(s0.raw()) + static_cast(s1.raw())); else return static_cast(static_cast(s0.raw()) - static_cast(s1.raw())); } else if constexpr (Scheme == sharing::additive) return static_cast(s0.raw() + s1.raw()); else return static_cast(s0.raw() - s1.raw()); } template HEDLEY_ALWAYS_INLINE HEDLEY_PURE HEDLEY_NO_THROW constexpr T reconstruct(const secret_share & s1, const secret_share & s0) noexcept { return reconstruct(s0, s1); } // --------------------------------------------------------------------------- // 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_{})); } // --------------------------------------------------------------------------- // 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 std::basic_ostream & operator<<( std::basic_ostream & os, const secret_share & s) { return os << s.raw(); } } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_SECRET_SHARE_HPP__