libdpf/include/dpf/secret_share.hpp
Ryan Henry 0d22946a0e Checkpoint the party/runtime stack before share-program and malicious-mode work.
Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-28 05:59:19 -06:00

1707 lines
55 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// @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__