libdpf/include/dpf/secret_share.hpp

1708 lines
55 KiB
C++
Raw Permalink Normal View History

/// @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 <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <ostream>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
#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<T, Party, 2, 3>`. Other thresholds
/// use `shamir::share<T, Party, K, N>`.
shamir = 5
};
template <typename T, std::size_t Party, sharing Scheme>
struct secret_share;
template <typename T, std::size_t Party>
using additive_share = secret_share<T, Party, sharing::additive>;
template <typename T, std::size_t Party>
using subtractive_share = secret_share<T, Party, sharing::subtractive>;
template <typename T, std::size_t Party>
using additive3_share = secret_share<T, Party, sharing::additive3>;
template <typename T, std::size_t Party>
using replicated_share = secret_share<T, Party, sharing::replicated>;
template <typename T, std::size_t Party>
using fss_share = secret_share<T, Party, sharing::fss>;
template <typename T, std::size_t Party>
using shamir_share = secret_share<T, Party, sharing::shamir>;
/// @brief `true` for (2,2) additive, subtractive, and FSS leaf shares.
template <sharing Scheme>
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 <sharing Scheme>
inline constexpr std::size_t sharing_parties_v =
is_two_party_sharing_v<Scheme> ? 2 : 3;
/// @brief Shares required to open `Scheme`.
template <sharing Scheme>
inline constexpr std::size_t sharing_threshold_v =
(Scheme == sharing::replicated || Scheme == sharing::shamir)
? 2
: sharing_parties_v<Scheme>;
template <typename T>
struct is_secret_share : std::false_type
{
};
template <typename T, std::size_t Party, sharing Scheme>
struct is_secret_share<secret_share<T, Party, Scheme>> : std::true_type
{
};
template <typename T>
inline constexpr bool is_secret_share_v = is_secret_share<std::decay_t<T>>::value;
template <typename T>
struct share_party;
template <typename T, std::size_t Party, sharing Scheme>
struct share_party<secret_share<T, Party, Scheme>>
: std::integral_constant<std::size_t, Party>
{
};
template <typename T>
inline constexpr std::size_t share_party_v = share_party<std::decay_t<T>>::value;
template <typename T>
struct share_scheme;
template <typename T, std::size_t Party, sharing Scheme>
struct share_scheme<secret_share<T, Party, Scheme>>
: std::integral_constant<sharing, Scheme>
{
};
template <typename T>
inline constexpr sharing share_scheme_v = share_scheme<std::decay_t<T>>::value;
template <typename T>
struct share_value_type;
template <typename T, std::size_t Party, sharing Scheme>
struct share_value_type<secret_share<T, Party, Scheme>>
{
using type = T;
};
template <typename T>
using share_value_type_t = typename share_value_type<std::decay_t<T>>::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 <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr T negate_word(const T & v) noexcept
{
if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
{
using unsigned_type = std::make_unsigned_t<T>;
return static_cast<T>(static_cast<unsigned_type>(0)
- static_cast<unsigned_type>(v));
}
else
return static_cast<T>(-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 <typename T>
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<T, float> || std::is_same_v<T, double>)
{
using bits_t = std::conditional_t<sizeof(T) == 4, std::uint32_t, std::uint64_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<bits_t>(xa ^ xb);
T out{};
std::memcpy(&out, &xc, sizeof(T));
return out;
}
else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
{
using unsigned_type = std::make_unsigned_t<T>;
return static_cast<T>(static_cast<unsigned_type>(a)
+ static_cast<unsigned_type>(b));
}
else
return static_cast<T>(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 <typename T>
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<T, float> || std::is_same_v<T, double>)
return group_add(a, b);
else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
{
using unsigned_type = std::make_unsigned_t<T>;
return static_cast<T>(static_cast<unsigned_type>(a)
- static_cast<unsigned_type>(b));
}
else
return static_cast<T>(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 <typename T>
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<T, float> || std::is_same_v<T, double>)
{
using bits_t = std::conditional_t<sizeof(T) == 4, std::uint32_t, std::uint64_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<bits_t>(xa & xb);
T out{};
std::memcpy(&out, &xc, sizeof(T));
return out;
}
else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
{
using unsigned_type = std::make_unsigned_t<T>;
return static_cast<T>(static_cast<unsigned_type>(a)
* static_cast<unsigned_type>(b));
}
else
return static_cast<T>(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 <sharing Scheme, std::size_t Party>
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 <sharing From, sharing To, std::size_t Party, typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr T retarget_word(const T & v) noexcept
{
if constexpr (two_party_sign_v<From, Party> == two_party_sign_v<To, Party>)
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 <typename T, std::size_t Party, sharing Scheme>
struct secret_share
{
static_assert(
(is_two_party_sharing_v<Scheme> && (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<T, Party> as_additive() const noexcept
{
static_assert(is_two_party_sharing_v<Scheme>,
"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<T, Party>::from_raw(value);
// Subtractive and FSS: party 0 keeps bits; party 1 negates.
return additive_share<T, Party>::from_raw(
detail::retarget_word<Scheme, sharing::additive, Party>(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<T, Party> as_subtractive() const noexcept
{
static_assert(is_two_party_sharing_v<Scheme>,
"as_subtractive converts a (2,2) share");
if constexpr (Scheme == sharing::subtractive)
return subtractive_share<T, Party>::from_raw(value);
// Additive party 1 negates. FSS already opens like subtractive.
return subtractive_share<T, Party>::from_raw(
detail::retarget_word<Scheme, sharing::subtractive, Party>(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<T, Party> as_fss() const noexcept
{
static_assert(is_two_party_sharing_v<Scheme>,
"as_fss converts a (2,2) share");
if constexpr (Scheme == sharing::fss)
return fss_share<T, Party>::from_raw(value);
return fss_share<T, Party>::from_raw(
detail::retarget_word<Scheme, sharing::fss, Party>(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 <sharing NewScheme, std::size_t NewParty = Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, NewParty, NewScheme> retag() const noexcept
{
return secret_share<T, NewParty, NewScheme>::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<T>(value + rhs.value);
return *this;
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr secret_share & operator-=(const secret_share & rhs) noexcept
{
value = static_cast<T>(value - rhs.value);
return *this;
}
template <typename Scalar,
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator*=(const Scalar & c) noexcept
{
value = static_cast<T>(value * static_cast<T>(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 <typename Plain,
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator+=(const Plain & c) noexcept
{
if constexpr (Party == 0)
value = static_cast<T>(value + static_cast<T>(c));
return *this;
}
template <typename Plain,
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator-=(const Plain & c) noexcept
{
if constexpr (Party == 0)
value = static_cast<T>(value - static_cast<T>(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 <typename T, std::size_t Party>
struct secret_share<T, Party, sharing::replicated>
{
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<T, Party> & mine,
const additive3_share<T, next_party> & 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<T, Party> as_additive3() const noexcept
{
return additive3_share<T, Party>::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<T, next_party> next_additive3() const noexcept
{
return additive3_share<T, next_party>::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 <typename Scalar,
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator*=(const Scalar & c) noexcept
{
own = static_cast<T>(own * static_cast<T>(c));
next = static_cast<T>(next * static_cast<T>(c));
return *this;
}
/// @brief Absorb a public plaintext into `x_0` only.
/// @tparam Plain plain
/// @param c the `c`
/// @return `*this`
template <typename Plain,
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator+=(const Plain & c) noexcept
{
const T addend = static_cast<T>(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 <typename Plain,
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator-=(const Plain & c) noexcept
{
const T subtrahend = static_cast<T>(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<T, Party, 2, 3>`, 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 <typename T, std::size_t Party>
struct secret_share<T, Party, sharing::shamir>
{
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<T>(-value));
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr secret_share & operator+=(const secret_share & rhs) noexcept
{
value = static_cast<T>(value + rhs.value);
return *this;
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr secret_share & operator-=(const secret_share & rhs) noexcept
{
value = static_cast<T>(value - rhs.value);
return *this;
}
template <typename Scalar,
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator*=(const Scalar & c) noexcept
{
value = static_cast<T>(value * static_cast<T>(c));
return *this;
}
/// @brief Absorb a public plaintext on every party.
/// @tparam Plain plain
/// @param c the `c`
/// @return `*this`
template <typename Plain,
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator+=(const Plain & c) noexcept
{
value = static_cast<T>(value + static_cast<T>(c));
return *this;
}
template <typename Plain,
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator-=(const Plain & c) noexcept
{
value = static_cast<T>(value - static_cast<T>(c));
return *this;
}
};
namespace shamir
{
/// @brief `(2,3)` is the `sharing::shamir` share, not `basic_share`.
template <typename T, std::size_t Party>
struct share_of<T, Party, 2, 3>
{
using type = secret_share<T, Party, sharing::shamir>;
};
template <typename T, std::size_t Party>
struct params<secret_share<T, Party, sharing::shamir>> : 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<T, Party, sharing::shamir>::point;
};
} // namespace shamir
// ---------------------------------------------------------------------------
// Same-scheme, same-party arithmetic
// ---------------------------------------------------------------------------
template <typename T, std::size_t Party, sharing Scheme>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator+(
secret_share<T, Party, Scheme> lhs,
const secret_share<T, Party, Scheme> & rhs) noexcept
{
lhs += rhs;
return lhs;
}
template <typename T, std::size_t Party, sharing Scheme>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator-(
secret_share<T, Party, Scheme> lhs,
const secret_share<T, Party, Scheme> & rhs) noexcept
{
lhs -= rhs;
return lhs;
}
template <typename T, std::size_t Party, sharing Scheme, typename Scalar,
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator*(
secret_share<T, Party, Scheme> lhs, const Scalar & c) noexcept
{
lhs *= c;
return lhs;
}
template <typename T, std::size_t Party, sharing Scheme, typename Scalar,
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator*(
const Scalar & c, secret_share<T, Party, Scheme> 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 <typename T, std::size_t Party, sharing LhsScheme, sharing RhsScheme,
std::enable_if_t<LhsScheme != RhsScheme
&& is_two_party_sharing_v<LhsScheme>
&& is_two_party_sharing_v<RhsScheme>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, LhsScheme> operator+(
const secret_share<T, Party, LhsScheme> & lhs,
const secret_share<T, Party, RhsScheme> & rhs) noexcept
{
if constexpr (detail::two_party_sign_v<LhsScheme, Party>
== detail::two_party_sign_v<RhsScheme, Party>)
return secret_share<T, Party, LhsScheme>::from_raw(
static_cast<T>(lhs.raw() + rhs.raw()));
else
return secret_share<T, Party, LhsScheme>::from_raw(
static_cast<T>(lhs.raw() - rhs.raw()));
}
template <typename T, std::size_t Party, sharing LhsScheme, sharing RhsScheme,
std::enable_if_t<LhsScheme != RhsScheme
&& is_two_party_sharing_v<LhsScheme>
&& is_two_party_sharing_v<RhsScheme>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, LhsScheme> operator-(
const secret_share<T, Party, LhsScheme> & lhs,
const secret_share<T, Party, RhsScheme> & rhs) noexcept
{
if constexpr (detail::two_party_sign_v<LhsScheme, Party>
== detail::two_party_sign_v<RhsScheme, Party>)
return secret_share<T, Party, LhsScheme>::from_raw(
static_cast<T>(lhs.raw() - rhs.raw()));
else
return secret_share<T, Party, LhsScheme>::from_raw(
static_cast<T>(lhs.raw() + rhs.raw()));
}
// ---------------------------------------------------------------------------
// Plaintext absorb (party 0 only)
// ---------------------------------------------------------------------------
template <typename T, std::size_t Party, sharing Scheme, typename Plain,
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator+(
secret_share<T, Party, Scheme> lhs, const Plain & c) noexcept
{
lhs += c;
return lhs;
}
template <typename T, std::size_t Party, sharing Scheme, typename Plain,
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator+(
const Plain & c, secret_share<T, Party, Scheme> rhs) noexcept
{
rhs += c;
return rhs;
}
template <typename T, std::size_t Party, sharing Scheme, typename Plain,
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator-(
secret_share<T, Party, Scheme> lhs, const Plain & c) noexcept
{
lhs -= c;
return lhs;
}
// ---------------------------------------------------------------------------
// Equality (same party, same scheme) — compare raw bits
// ---------------------------------------------------------------------------
template <typename T, std::size_t Party, sharing Scheme,
std::enable_if_t<Scheme != sharing::replicated, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr bool operator==(const secret_share<T, Party, Scheme> & lhs,
const secret_share<T, Party, Scheme> & rhs) noexcept
{
return lhs.raw() == rhs.raw();
}
template <typename T, std::size_t Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr bool operator==(const replicated_share<T, Party> & lhs,
const replicated_share<T, Party> & rhs) noexcept
{
return lhs.own == rhs.own && lhs.next == rhs.next;
}
template <typename T, std::size_t Party, sharing Scheme>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr bool operator!=(const secret_share<T, Party, Scheme> & lhs,
const secret_share<T, Party, Scheme> & rhs) noexcept
{
return !(lhs == rhs);
}
// ---------------------------------------------------------------------------
// Reconstruction
// ---------------------------------------------------------------------------
template <typename T, sharing Scheme,
std::enable_if_t<is_two_party_sharing_v<Scheme>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr T reconstruct(const secret_share<T, 0, Scheme> & s0,
const secret_share<T, 1, Scheme> & 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 <typename T, sharing Scheme,
std::enable_if_t<is_two_party_sharing_v<Scheme>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr T reconstruct(const secret_share<T, 1, Scheme> & s1,
const secret_share<T, 0, Scheme> & s0) noexcept
{
return reconstruct(s0, s1);
}
namespace detail
{
template <std::size_t Party, typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr void store_additive3(T & c0, T & c1, T & c2,
const additive3_share<T, Party> & share) noexcept
{
if constexpr (Party == 0)
c0 = share.raw();
else if constexpr (Party == 1)
c1 = share.raw();
else
c2 = share.raw();
}
template <std::size_t Party, typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr void store_replicated_own(T & c0, T & c1, T & c2,
const replicated_share<T, Party> & 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 <typename T, std::size_t P, std::size_t Q, std::size_t R>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr T reconstruct(const additive3_share<T, P> & a,
const additive3_share<T, Q> & b,
const additive3_share<T, R> & 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 <typename T, std::size_t P, std::size_t Q>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr T reconstruct(const replicated_share<T, P> & a,
const replicated_share<T, Q> & 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 <typename T, std::size_t P, std::size_t Q, std::size_t R>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr T reconstruct(const replicated_share<T, P> & a,
const replicated_share<T, Q> & b,
const replicated_share<T, R> & 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<T>`
/// @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 <typename T, std::size_t P, std::size_t Q>
HEDLEY_WARN_UNUSED_RESULT
T reconstruct(const shamir_share<T, P> & a, const shamir_share<T, Q> & 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 <typename T, std::size_t P, std::size_t Q, std::size_t R>
HEDLEY_WARN_UNUSED_RESULT
T reconstruct(const shamir_share<T, P> & a, const shamir_share<T, Q> & b,
const shamir_share<T, R> & c)
{
return shamir::reconstruct(a, b, c);
}
// ---------------------------------------------------------------------------
// Plaintext splits (share1 = 0)
// ---------------------------------------------------------------------------
template <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
constexpr auto make_additive_shares(T secret) noexcept
{
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
return std::make_pair(
additive_share<T_, 0>::from_raw(static_cast<T_>(secret)),
additive_share<T_, 1>::from_raw(T_{}));
}
template <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
constexpr auto make_subtractive_shares(T secret) noexcept
{
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
return std::make_pair(
subtractive_share<T_, 0>::from_raw(static_cast<T_>(secret)),
subtractive_share<T_, 1>::from_raw(T_{}));
}
template <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
constexpr auto make_additive3_shares(T secret) noexcept
{
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
return std::make_tuple(
additive3_share<T_, 0>::from_raw(static_cast<T_>(secret)),
additive3_share<T_, 1>::from_raw(T_{}),
additive3_share<T_, 2>::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<T, K, N>`.
/// @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 <std::size_t K, std::size_t N, typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
constexpr auto make_shamir_shares(T secret,
const std::array<std::remove_cv_t<std::remove_reference_t<T>>,
shamir::access<K, N>::degree> & coeff) noexcept
{
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
return shamir::deal<T_, K, N>(static_cast<T_>(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 <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
constexpr auto make_shamir_shares(T secret, T slope) noexcept
{
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
return make_shamir_shares<2, 3>(static_cast<T_>(secret),
std::array<T_, 1>{{static_cast<T_>(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 <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
constexpr auto make_replicated_shares(T secret) noexcept
{
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
const T_ s = static_cast<T_>(secret);
const T_ z{};
return std::make_tuple(
replicated_share<T_, 0>::from_raw(s, z),
replicated_share<T_, 1>::from_raw(z, z),
replicated_share<T_, 2>::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 <typename T>
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<std::remove_reference_t<T>>;
const T_ a = static_cast<T_>(x0);
const T_ b = static_cast<T_>(x1);
const T_ c = static_cast<T_>(x2);
return std::make_tuple(
replicated_share<T_, 0>::from_raw(a, b),
replicated_share<T_, 1>::from_raw(b, c),
replicated_share<T_, 2>::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 <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
constexpr auto make_replicated_shares(const additive3_share<T, 0> & s0,
const additive3_share<T, 1> & s1,
const additive3_share<T, 2> & 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 <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr auto add_replicated(
replicated_share<T, 0> r0,
replicated_share<T, 1> r1,
replicated_share<T, 2> r2,
const additive3_share<T, 0> & a0,
const additive3_share<T, 1> & a1,
const additive3_share<T, 2> & 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 <typename T, std::size_t Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr subtractive_share<T, Party> a2b(const additive_share<T, Party> & s) noexcept
{
return s.as_subtractive();
}
/// @brief (2,2) subtractive to additive. Party 1 negates.
template <typename T, std::size_t Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr additive_share<T, Party> b2a(const subtractive_share<T, Party> & s) noexcept
{
return s.as_additive();
}
/// @brief (2,2) additive to an FSS leaf share. Party 1 negates.
template <typename T, std::size_t Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr fss_share<T, Party> a2fss(const additive_share<T, Party> & s) noexcept
{
return s.as_fss();
}
/// @brief FSS leaf share to (2,2) additive. Party 1 negates.
template <typename T, std::size_t Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr additive_share<T, Party> fss2a(const fss_share<T, Party> & s) noexcept
{
return s.as_additive();
}
/// @brief (2,2) subtractive to an FSS leaf share. Same opening, bits unchanged.
template <typename T, std::size_t Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr fss_share<T, Party> b2fss(const subtractive_share<T, Party> & s) noexcept
{
return s.as_fss();
}
/// @brief FSS leaf share to (2,2) subtractive. Same opening, bits unchanged.
template <typename T, std::size_t Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr subtractive_share<T, Party> fss2b(const fss_share<T, Party> & s) noexcept
{
return s.as_subtractive();
}
/// @brief This party's (3,3) component of a replicated share (`y` = additive3).
template <typename T, std::size_t Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr additive3_share<T, Party> rss2y(const replicated_share<T, Party> & s) noexcept
{
return s.as_additive3();
}
/// @brief One replicated share from this party's component and the next party's.
template <typename T, std::size_t Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr replicated_share<T, Party> y2rss(
const additive3_share<T, Party> & mine,
const additive3_share<T, replicated_share<T, Party>::next_party> & nxt) noexcept
{
return replicated_share<T, Party>::from_additive3(mine, nxt);
}
/// @brief Replicated shares of a (3,3)-additive sharing.
template <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr auto y2rss(const additive3_share<T, 0> & y0,
const additive3_share<T, 1> & y1,
const additive3_share<T, 2> & y2) noexcept
{
return make_replicated_shares(y0, y1, y2);
}
/// @brief The three (3,3) components of a replicated sharing.
template <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr auto rss2y(const replicated_share<T, 0> & r0,
const replicated_share<T, 1> & r1,
const replicated_share<T, 2> & 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 <typename T, std::size_t P, std::size_t Q>
HEDLEY_WARN_UNUSED_RESULT
auto s2y(const shamir_share<T, P> & a, const shamir_share<T, Q> & b)
{
return make_additive3_shares(reconstruct(a, b));
}
/// @brief Shamir-share a (3,3) additive secret with `slope`.
template <typename T>
HEDLEY_WARN_UNUSED_RESULT
auto y2s(const additive3_share<T, 0> & y0, const additive3_share<T, 1> & y1,
const additive3_share<T, 2> & 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 <typename T, std::size_t P, std::size_t Q>
HEDLEY_WARN_UNUSED_RESULT
auto s2rss(const shamir_share<T, P> & a, const shamir_share<T, Q> & b)
{
return make_replicated_shares(reconstruct(a, b));
}
/// @brief Shamir-share a replicated secret with `slope`.
template <typename T, std::size_t P, std::size_t Q>
HEDLEY_WARN_UNUSED_RESULT
auto rss2s(const replicated_share<T, P> & a, const replicated_share<T, Q> & 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 <typename T, std::size_t Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr additive3_share<T, Party> rss_mul(
const replicated_share<T, Party> & x,
const replicated_share<T, Party> & 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<T, Party>::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 <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr auto rss_mul(
const replicated_share<T, 0> & x0, const replicated_share<T, 1> & x1,
const replicated_share<T, 2> & x2, const replicated_share<T, 0> & y0,
const replicated_share<T, 1> & y1, const replicated_share<T, 2> & 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 <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr auto rss_mul(
const replicated_share<T, 0> & x0, const replicated_share<T, 1> & x1,
const replicated_share<T, 2> & x2, const replicated_share<T, 0> & y0,
const replicated_share<T, 1> & y1, const replicated_share<T, 2> & y2,
const additive3_share<T, 0> & m0, const additive3_share<T, 1> & m1,
const additive3_share<T, 2> & m2) noexcept
{
return make_replicated_shares(
additive3_share<T, 0>::from_raw(
detail::group_add(rss_mul(x0, y0).raw(), m0.raw())),
additive3_share<T, 1>::from_raw(
detail::group_add(rss_mul(x1, y1).raw(), m1.raw())),
additive3_share<T, 2>::from_raw(
detail::group_add(rss_mul(x2, y2).raw(), m2.raw())));
}
// ---------------------------------------------------------------------------
// Party-tagged DPF key wrapper
// ---------------------------------------------------------------------------
template <typename T>
struct is_party_key : std::false_type
{
};
template <std::size_t Party, typename Key>
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<std::size_t>(
static_cast<bool>(dpf::get_lo_bit(this->root()))) == Party);
#endif
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
Key & key() noexcept { return static_cast<Key &>(*this); }
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
const Key & key() const noexcept { return static_cast<const Key &>(*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<std::uint64_t, Party>::from_raw(
Key::cmp_addend());
}
};
template <std::size_t Party, typename Key>
struct is_party_key<party_key<Party, Key>> : std::true_type
{
};
template <typename T>
inline constexpr bool is_party_key_v = is_party_key<std::decay_t<T>>::value;
template <typename T>
struct party_of; // incomplete for non-`party_key` (fail loudly on misuse)
template <std::size_t Party, typename Key>
struct party_of<party_key<Party, Key>>
: std::integral_constant<std::size_t, Party>
{
};
template <typename T>
inline constexpr std::size_t party_of_v = party_of<std::decay_t<T>>::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 <typename T>
struct unwrap_party_key
{
using type = std::decay_t<T>;
};
template <std::size_t Party, typename Key>
struct unwrap_party_key<party_key<Party, Key>>
{
using type = Key;
};
template <typename T>
using unwrap_party_key_t = typename unwrap_party_key<std::decay_t<T>>::type;
template <std::size_t Party, typename Key>
HEDLEY_ALWAYS_INLINE
auto make_party_key(Key && k)
{
return party_key<Party, std::decay_t<Key>>(std::forward<Key>(k));
}
template <typename Key0, typename Key1>
HEDLEY_ALWAYS_INLINE
auto make_party_key_pair(Key0 && k0, Key1 && k1)
{
using K = std::decay_t<Key0>;
static_assert(std::is_same_v<K, std::decay_t<Key1>>,
"make_party_key_pair: both keys must have the same type");
return std::make_pair(
party_key<0, K>(std::forward<Key0>(k0)),
party_key<1, K>(std::forward<Key1>(k1)));
}
template <typename CharT, typename Traits, typename T, std::size_t Party,
sharing Scheme,
std::enable_if_t<Scheme != sharing::replicated, int> = 0>
std::basic_ostream<CharT, Traits> & operator<<(
std::basic_ostream<CharT, Traits> & os,
const secret_share<T, Party, Scheme> & s)
{
return os << s.raw();
}
template <typename CharT, typename Traits, typename T, std::size_t Party>
std::basic_ostream<CharT, Traits> & operator<<(
std::basic_ostream<CharT, Traits> & os,
const replicated_share<T, Party> & 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 <typename Slot, typename Val>
HEDLEY_ALWAYS_INLINE
constexpr void assign_share_slot(Slot && slot, Val && val)
{
using slot_t = std::decay_t<Slot>;
using val_t = std::decay_t<Val>;
if constexpr (is_secret_share_v<slot_t>)
{
if constexpr (is_secret_share_v<val_t>)
{
if constexpr (share_scheme_v<slot_t> == sharing::replicated)
{
static_assert(share_scheme_v<val_t> == sharing::replicated,
"assign_share_slot: replicated slot needs a replicated share");
static_assert(share_party_v<slot_t> == share_party_v<val_t>,
"assign_share_slot: replicated parties differ");
slot = slot_t::from_raw(val.own, val.next);
}
else
{
static_assert(share_scheme_v<val_t> != sharing::replicated,
"assign_share_slot: a replicated share has two components");
slot = slot_t::from_raw(val.raw());
}
}
else
{
static_assert(share_scheme_v<slot_t> != sharing::replicated,
"assign_share_slot: a plaintext is not a replicated share");
slot = slot_t::from_raw(
static_cast<typename slot_t::value_type>(val));
}
}
else if constexpr (is_secret_share_v<val_t>)
{
static_assert(share_scheme_v<val_t> != sharing::replicated,
"assign_share_slot: open a replicated share, or copy own and next");
slot = val.raw();
}
else
slot = std::forward<Val>(val);
}
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_SECRET_SHARE_HPP__