565 lines
19 KiB
C++
565 lines
19 KiB
C++
|
|
/// @file dpf/shamir.hpp
|
||
|
|
/// @brief (K,N) Shamir secret sharing over a field.
|
||
|
|
/// @details The secret is the constant term of a polynomial of degree `K-1`.
|
||
|
|
/// Party `i` (0-based) holds that polynomial at `x = i+1`. Any `K`
|
||
|
|
/// shares reconstruct by Lagrange at `0`. Further shares are checked
|
||
|
|
/// against the polynomial of the first `K`; they are not an error
|
||
|
|
/// correction. Exactly `K` shares are not checked. When `K = N` that
|
||
|
|
/// is every share. A full set of shares of a different secret is
|
||
|
|
/// consistent with itself. `(2,3)`
|
||
|
|
/// is `two_of_three`. That case is the type `shamir_share<T, Party>`
|
||
|
|
/// (`sharing::shamir`), and `shamir3` is the same polynomial on
|
||
|
|
/// `fp61`. Uniform coefficients are drawn by `shamir::share_secret`
|
||
|
|
/// in `random.hpp`. A complete program is `examples/mwe/shamir.cpp`.
|
||
|
|
/// @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_SHAMIR_HPP__
|
||
|
|
#define LIBDPF_INCLUDE_DPF_SHAMIR_HPP__
|
||
|
|
|
||
|
|
#include <array>
|
||
|
|
#include <cstddef>
|
||
|
|
#include <cstdint>
|
||
|
|
#include <stdexcept>
|
||
|
|
#include <tuple>
|
||
|
|
#include <type_traits>
|
||
|
|
#include <utility>
|
||
|
|
|
||
|
|
#include "hedley/hedley.h"
|
||
|
|
|
||
|
|
namespace dpf
|
||
|
|
{
|
||
|
|
|
||
|
|
namespace detail
|
||
|
|
{
|
||
|
|
|
||
|
|
/// @brief Field inverse used by Shamir reconstruction.
|
||
|
|
/// @details Specialize for a field. `fp61` is specialized in `fp61.hpp`.
|
||
|
|
/// @tparam T value type
|
||
|
|
template <typename T>
|
||
|
|
struct shamir_field : std::false_type
|
||
|
|
{
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace detail
|
||
|
|
|
||
|
|
namespace shamir
|
||
|
|
{
|
||
|
|
|
||
|
|
/// @brief Access structure: any `K` of `N` shares open the secret.
|
||
|
|
/// @tparam K reconstruction threshold, at least 1
|
||
|
|
/// @tparam N shareholder count, at least `K`
|
||
|
|
template <std::size_t K, std::size_t N>
|
||
|
|
struct access
|
||
|
|
{
|
||
|
|
static_assert(K >= 1, "shamir threshold is at least 1");
|
||
|
|
static_assert(N >= K, "shamir threshold cannot exceed the shareholder count");
|
||
|
|
|
||
|
|
/// @brief Shares required to open the secret.
|
||
|
|
static constexpr std::size_t threshold = K;
|
||
|
|
/// @brief Shareholders who receive a point.
|
||
|
|
static constexpr std::size_t parties = N;
|
||
|
|
/// @brief Polynomial degree, `K - 1`.
|
||
|
|
static constexpr std::size_t degree = K - 1;
|
||
|
|
};
|
||
|
|
|
||
|
|
/// @brief The (2,3) access structure behind `sharing::shamir` and `shamir3`.
|
||
|
|
using two_of_three = access<2, 3>;
|
||
|
|
|
||
|
|
/// @brief One shareholder's value at a runtime evaluation point.
|
||
|
|
/// @tparam T field element
|
||
|
|
template <typename T>
|
||
|
|
struct point_share
|
||
|
|
{
|
||
|
|
/// @brief Evaluation point in `1 .. N`. Party `i` uses point `i + 1`.
|
||
|
|
int point = 1;
|
||
|
|
/// @brief `p(point)`, where `p(0)` is the secret.
|
||
|
|
T value{};
|
||
|
|
};
|
||
|
|
|
||
|
|
/// @brief Traits of a typed Shamir share. The primary is not a Shamir share.
|
||
|
|
template <typename Share, typename Enable = void>
|
||
|
|
struct params : std::false_type
|
||
|
|
{
|
||
|
|
using value_type = void;
|
||
|
|
static constexpr std::size_t party = 0;
|
||
|
|
static constexpr std::size_t threshold = 0;
|
||
|
|
static constexpr std::size_t parties = 0;
|
||
|
|
static constexpr std::uint64_t point = 0;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <typename Share>
|
||
|
|
inline constexpr bool is_share_v = params<std::decay_t<Share>>::value;
|
||
|
|
|
||
|
|
namespace detail
|
||
|
|
{
|
||
|
|
|
||
|
|
template <typename T, std::size_t Degree>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_CONST
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr T horner(T secret, const std::array<T, Degree> & coeff,
|
||
|
|
std::uint64_t x) noexcept
|
||
|
|
{
|
||
|
|
// p(x) = secret + c[0] x + c[1] x^2 + ... + c[Degree-1] x^Degree.
|
||
|
|
T high{};
|
||
|
|
const T tx{x};
|
||
|
|
for (std::size_t k = 0; k < Degree; ++k)
|
||
|
|
{
|
||
|
|
const std::size_t i = Degree - 1 - k;
|
||
|
|
high = static_cast<T>(static_cast<T>(high * tx) + coeff[i]);
|
||
|
|
}
|
||
|
|
return static_cast<T>(static_cast<T>(high * tx) + secret);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <std::size_t M>
|
||
|
|
HEDLEY_CONST
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr bool distinct_points(const std::array<std::uint64_t, M> & xs,
|
||
|
|
std::size_t parties) noexcept
|
||
|
|
{
|
||
|
|
for (std::size_t i = 0; i < M; ++i)
|
||
|
|
{
|
||
|
|
if (xs[i] < 1 || xs[i] > parties)
|
||
|
|
return false;
|
||
|
|
for (std::size_t j = 0; j < i; ++j)
|
||
|
|
if (xs[i] == xs[j])
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T>
|
||
|
|
T lagrange(T at, const std::uint64_t * xs, const T * ys, std::size_t k)
|
||
|
|
{
|
||
|
|
T secret{};
|
||
|
|
const T one{1};
|
||
|
|
for (std::size_t i = 0; i < k; ++i)
|
||
|
|
{
|
||
|
|
T num = one;
|
||
|
|
T den = one;
|
||
|
|
const T xi{xs[i]};
|
||
|
|
for (std::size_t j = 0; j < k; ++j)
|
||
|
|
{
|
||
|
|
if (i == j)
|
||
|
|
continue;
|
||
|
|
const T xj{xs[j]};
|
||
|
|
num = static_cast<T>(num * static_cast<T>(at - xj));
|
||
|
|
den = static_cast<T>(den * static_cast<T>(xi - xj));
|
||
|
|
}
|
||
|
|
if (den == T{})
|
||
|
|
throw std::invalid_argument(
|
||
|
|
"shamir: evaluation points collide in the field");
|
||
|
|
const T weight = static_cast<T>(
|
||
|
|
num * ::dpf::detail::shamir_field<T>::inv(den));
|
||
|
|
secret = static_cast<T>(secret + static_cast<T>(ys[i] * weight));
|
||
|
|
}
|
||
|
|
return secret;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, std::size_t K, std::size_t N>
|
||
|
|
T open_points(const std::uint64_t * xs, const T * ys, std::size_t count)
|
||
|
|
{
|
||
|
|
static_assert(::dpf::detail::shamir_field<T>::value,
|
||
|
|
"shamir reconstruct: specialize detail::shamir_field<T>");
|
||
|
|
if (count < K || count > N)
|
||
|
|
throw std::invalid_argument(
|
||
|
|
"shamir: the number of shares must be between K and N");
|
||
|
|
for (std::size_t i = 0; i < count; ++i)
|
||
|
|
{
|
||
|
|
if (xs[i] < 1 || xs[i] > N)
|
||
|
|
throw std::invalid_argument(
|
||
|
|
"shamir: evaluation point is outside 1..N");
|
||
|
|
for (std::size_t j = 0; j < i; ++j)
|
||
|
|
if (xs[i] == xs[j])
|
||
|
|
throw std::invalid_argument("shamir: duplicate evaluation point");
|
||
|
|
// x = 0 is the secret. A field that folds the integer point onto 0
|
||
|
|
// (gf2 with N > 1) would hand that party the secret.
|
||
|
|
if (T{xs[i]} == T{})
|
||
|
|
throw std::invalid_argument(
|
||
|
|
"shamir: evaluation point is zero in the field");
|
||
|
|
}
|
||
|
|
const T secret = lagrange(T{}, xs, ys, K);
|
||
|
|
for (std::size_t extra = K; extra < count; ++extra)
|
||
|
|
{
|
||
|
|
if (lagrange(T{xs[extra]}, xs, ys, K) != ys[extra])
|
||
|
|
throw std::runtime_error("shamir: inconsistent shares");
|
||
|
|
}
|
||
|
|
return secret;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, std::size_t K, std::size_t N>
|
||
|
|
T open_runtime(const point_share<T> * shares, std::size_t count)
|
||
|
|
{
|
||
|
|
std::array<std::uint64_t, N> xs{};
|
||
|
|
std::array<T, N> ys{};
|
||
|
|
if (count < K || count > N)
|
||
|
|
throw std::invalid_argument(
|
||
|
|
"shamir: the number of shares must be between K and N");
|
||
|
|
for (std::size_t i = 0; i < count; ++i)
|
||
|
|
{
|
||
|
|
if (shares[i].point < 1
|
||
|
|
|| static_cast<std::size_t>(shares[i].point) > N)
|
||
|
|
throw std::invalid_argument(
|
||
|
|
"shamir: evaluation point is outside 1..N");
|
||
|
|
xs[i] = static_cast<std::uint64_t>(shares[i].point);
|
||
|
|
ys[i] = shares[i].value;
|
||
|
|
}
|
||
|
|
return open_points<T, K, N>(xs.data(), ys.data(), count);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace detail
|
||
|
|
|
||
|
|
/// @brief Share of a `(K,N)` Shamir secret at a compile-time party.
|
||
|
|
/// @details `(2,3)` is not this type. It is `shamir_share<T, Party>`.
|
||
|
|
/// @tparam T field element
|
||
|
|
/// @tparam Party 0-based party index, in `0 .. N-1`
|
||
|
|
/// @tparam K reconstruction threshold
|
||
|
|
/// @tparam N shareholder count
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N>
|
||
|
|
struct basic_share
|
||
|
|
{
|
||
|
|
static_assert(!(K == 2 && N == 3),
|
||
|
|
"(2,3) Shamir is shamir::share<T, Party, 2, 3> (dpf::shamir_share)");
|
||
|
|
static_assert(Party < N, "shamir party must be in 0 .. N-1");
|
||
|
|
|
||
|
|
using value_type = T;
|
||
|
|
using access_type = access<K, N>;
|
||
|
|
static constexpr std::size_t party = Party;
|
||
|
|
static constexpr std::size_t threshold = K;
|
||
|
|
static constexpr std::size_t parties = N;
|
||
|
|
static constexpr std::size_t degree = access_type::degree;
|
||
|
|
/// @brief Lagrange point. Party 0 is point 1.
|
||
|
|
static constexpr std::uint64_t point = Party + 1;
|
||
|
|
|
||
|
|
T value{};
|
||
|
|
|
||
|
|
/// @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 basic_share from_raw(T v) noexcept
|
||
|
|
{
|
||
|
|
basic_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 basic_share operator-() const noexcept
|
||
|
|
{
|
||
|
|
return from_raw(static_cast<T>(-value));
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
constexpr basic_share & operator+=(const basic_share & rhs) noexcept
|
||
|
|
{
|
||
|
|
value = static_cast<T>(value + rhs.value);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
constexpr basic_share & operator-=(const basic_share & rhs) noexcept
|
||
|
|
{
|
||
|
|
value = static_cast<T>(value - rhs.value);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename Scalar,
|
||
|
|
std::enable_if_t<!is_share_v<Scalar>
|
||
|
|
&& std::is_convertible_v<Scalar, T>, int> = 0>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr basic_share & operator*=(const Scalar & c) noexcept
|
||
|
|
{
|
||
|
|
value = static_cast<T>(value * static_cast<T>(c));
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Absorb a public plaintext. Every point of `p` grows by `c`.
|
||
|
|
template <typename Plain,
|
||
|
|
std::enable_if_t<!is_share_v<Plain>
|
||
|
|
&& std::is_convertible_v<Plain, T>, int> = 0>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr basic_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_share_v<Plain>
|
||
|
|
&& std::is_convertible_v<Plain, T>, int> = 0>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr basic_share & operator-=(const Plain & c) noexcept
|
||
|
|
{
|
||
|
|
value = static_cast<T>(value - static_cast<T>(c));
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N>
|
||
|
|
struct params<basic_share<T, Party, K, N>> : std::true_type
|
||
|
|
{
|
||
|
|
using value_type = T;
|
||
|
|
static constexpr std::size_t party = Party;
|
||
|
|
static constexpr std::size_t threshold = K;
|
||
|
|
static constexpr std::size_t parties = N;
|
||
|
|
static constexpr std::uint64_t point = Party + 1;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr basic_share<T, Party, K, N> operator+(
|
||
|
|
basic_share<T, Party, K, N> lhs,
|
||
|
|
const basic_share<T, Party, K, N> & rhs) noexcept
|
||
|
|
{
|
||
|
|
lhs += rhs;
|
||
|
|
return lhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr basic_share<T, Party, K, N> operator-(
|
||
|
|
basic_share<T, Party, K, N> lhs,
|
||
|
|
const basic_share<T, Party, K, N> & rhs) noexcept
|
||
|
|
{
|
||
|
|
lhs -= rhs;
|
||
|
|
return lhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N, typename Scalar,
|
||
|
|
std::enable_if_t<!is_share_v<Scalar> && std::is_convertible_v<Scalar, T>, int> = 0>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr basic_share<T, Party, K, N> operator*(
|
||
|
|
basic_share<T, Party, K, N> lhs, const Scalar & c) noexcept
|
||
|
|
{
|
||
|
|
lhs *= c;
|
||
|
|
return lhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N, typename Scalar,
|
||
|
|
std::enable_if_t<!is_share_v<Scalar> && std::is_convertible_v<Scalar, T>, int> = 0>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr basic_share<T, Party, K, N> operator*(
|
||
|
|
const Scalar & c, basic_share<T, Party, K, N> rhs) noexcept
|
||
|
|
{
|
||
|
|
rhs *= c;
|
||
|
|
return rhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N, typename Plain,
|
||
|
|
std::enable_if_t<!is_share_v<Plain> && std::is_convertible_v<Plain, T>, int> = 0>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr basic_share<T, Party, K, N> operator+(
|
||
|
|
basic_share<T, Party, K, N> lhs, const Plain & c) noexcept
|
||
|
|
{
|
||
|
|
lhs += c;
|
||
|
|
return lhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N, typename Plain,
|
||
|
|
std::enable_if_t<!is_share_v<Plain> && std::is_convertible_v<Plain, T>, int> = 0>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr basic_share<T, Party, K, N> operator+(
|
||
|
|
const Plain & c, basic_share<T, Party, K, N> rhs) noexcept
|
||
|
|
{
|
||
|
|
rhs += c;
|
||
|
|
return rhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N, typename Plain,
|
||
|
|
std::enable_if_t<!is_share_v<Plain> && std::is_convertible_v<Plain, T>, int> = 0>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr basic_share<T, Party, K, N> operator-(
|
||
|
|
basic_share<T, Party, K, N> lhs, const Plain & c) noexcept
|
||
|
|
{
|
||
|
|
lhs -= c;
|
||
|
|
return lhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr bool operator==(const basic_share<T, Party, K, N> & lhs,
|
||
|
|
const basic_share<T, Party, K, N> & rhs) noexcept
|
||
|
|
{
|
||
|
|
return lhs.raw() == rhs.raw();
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr bool operator!=(const basic_share<T, Party, K, N> & lhs,
|
||
|
|
const basic_share<T, Party, K, N> & rhs) noexcept
|
||
|
|
{
|
||
|
|
return !(lhs == rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Maps `(T, Party, K, N)` to the share type.
|
||
|
|
/// @details `(2,3)` is specialized to `shamir_share` in `secret_share.hpp`.
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N>
|
||
|
|
struct share_of
|
||
|
|
{
|
||
|
|
using type = basic_share<T, Party, K, N>;
|
||
|
|
};
|
||
|
|
|
||
|
|
/// @brief Share of a `(K,N)` Shamir secret at party `Party`.
|
||
|
|
/// @details `share<T, Party, 2, 3>` is `shamir_share<T, Party>`.
|
||
|
|
template <typename T, std::size_t Party, std::size_t K, std::size_t N>
|
||
|
|
using share = typename share_of<T, Party, K, N>::type;
|
||
|
|
|
||
|
|
namespace detail
|
||
|
|
{
|
||
|
|
|
||
|
|
template <typename T, std::size_t K, std::size_t N, std::size_t... Party>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_CONST
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr auto deal_at(T secret,
|
||
|
|
const std::array<T, access<K, N>::degree> & coeff,
|
||
|
|
std::index_sequence<Party...>) noexcept
|
||
|
|
{
|
||
|
|
return std::make_tuple(share<T, Party, K, N>::from_raw(
|
||
|
|
horner(secret, coeff, static_cast<std::uint64_t>(Party + 1)))...);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace detail
|
||
|
|
|
||
|
|
/// @brief Share `secret` at points `1 .. N`.
|
||
|
|
/// @details `p(x) = secret + coeff[0] x + ... + coeff[K-2] x^{K-1}`.
|
||
|
|
/// Party `i` stores `p(i+1)`. A zero coefficient is allowed.
|
||
|
|
/// Threshold 1 takes an empty coefficient array and copies `secret`.
|
||
|
|
/// `(2,3)` returns `shamir_share`s. `make_shamir_shares(secret, slope)`
|
||
|
|
/// is `deal<T, 2, 3>` with that one coefficient.
|
||
|
|
/// @tparam K reconstruction threshold
|
||
|
|
/// @tparam N shareholder count
|
||
|
|
/// @tparam T field type. `+` and `*` are the field operations. Opening also
|
||
|
|
/// needs `detail::shamir_field<T>`
|
||
|
|
/// @param secret the constant term
|
||
|
|
/// @param coeff higher coefficients, low degree first
|
||
|
|
/// @return shares for parties `0 .. N-1`
|
||
|
|
/// @see shamir::share_secret
|
||
|
|
/// \complexity O(NK) field operations. No messages.
|
||
|
|
template <typename T, std::size_t K, std::size_t N>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_CONST
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr auto deal(T secret,
|
||
|
|
const std::array<T, access<K, N>::degree> & coeff) noexcept
|
||
|
|
{
|
||
|
|
return detail::deal_at<T, K, N>(
|
||
|
|
secret, coeff, std::make_index_sequence<N>{});
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Open typed shares of one `(K,N)` scheme.
|
||
|
|
/// @details Pass at least `K` and at most `N` shares. The first `K` are the
|
||
|
|
/// interpolating set and are not checked against each other. Each
|
||
|
|
/// further share must lie on that polynomial. A lie among the first
|
||
|
|
/// `K` is reported only when an honest extra share is present. A
|
||
|
|
/// complete set of shares of some other secret does not throw.
|
||
|
|
/// @tparam Share0 first share
|
||
|
|
/// @tparam Rest the other shares
|
||
|
|
/// @param first first share
|
||
|
|
/// @param rest the other shares
|
||
|
|
/// @return the secret
|
||
|
|
/// @throws std::invalid_argument if a Lagrange denominator is zero
|
||
|
|
/// @throws std::runtime_error if an extra share misses the polynomial
|
||
|
|
/// \complexity O(MK^2) field operations for M shares. The shares are already in hand; this function does not exchange them.
|
||
|
|
template <typename Share0, typename... Rest,
|
||
|
|
std::enable_if_t<is_share_v<Share0>, int> = 0>
|
||
|
|
HEDLEY_WARN_UNUSED_RESULT
|
||
|
|
auto reconstruct(const Share0 & first, const Rest &... rest)
|
||
|
|
{
|
||
|
|
using info = params<std::decay_t<Share0>>;
|
||
|
|
using value_type = typename info::value_type;
|
||
|
|
constexpr std::size_t M = 1 + sizeof...(Rest);
|
||
|
|
static_assert(M >= info::threshold && M <= info::parties,
|
||
|
|
"shamir reconstruct: the number of shares must be between K and N");
|
||
|
|
static_assert(((is_share_v<Rest>
|
||
|
|
&& params<std::decay_t<Rest>>::threshold == info::threshold
|
||
|
|
&& params<std::decay_t<Rest>>::parties == info::parties
|
||
|
|
&& std::is_same_v<
|
||
|
|
typename params<std::decay_t<Rest>>::value_type, value_type>) && ...),
|
||
|
|
"shamir reconstruct: shares must be one (K,N) scheme");
|
||
|
|
constexpr std::array<std::uint64_t, M> xs{{
|
||
|
|
info::point, params<std::decay_t<Rest>>::point...}};
|
||
|
|
static_assert(detail::distinct_points(xs, info::parties),
|
||
|
|
"shamir reconstruct: need distinct parties in 0 .. N-1");
|
||
|
|
const std::array<value_type, M> ys{{first.raw(), rest.raw()...}};
|
||
|
|
return detail::open_points<value_type, info::threshold, info::parties>(
|
||
|
|
xs.data(), ys.data(), M);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Open runtime point shares. `shares` has length `count`.
|
||
|
|
/// @tparam T field type. Requires `detail::shamir_field<T>`
|
||
|
|
/// @tparam K reconstruction threshold
|
||
|
|
/// @tparam N shareholder count
|
||
|
|
/// @param shares evaluation points and values
|
||
|
|
/// @param count length of `shares`, in `K .. N`
|
||
|
|
/// @return the secret
|
||
|
|
/// @throws std::invalid_argument if the count or the points are illegal
|
||
|
|
/// @throws std::runtime_error if an extra share misses the polynomial of the first K
|
||
|
|
/// \complexity O(MK^2) field operations for M shares. The shares are already in hand; this function does not exchange them.
|
||
|
|
template <typename T, std::size_t K, std::size_t N>
|
||
|
|
HEDLEY_WARN_UNUSED_RESULT
|
||
|
|
T reconstruct(const point_share<T> * shares, std::size_t count)
|
||
|
|
{
|
||
|
|
return detail::open_runtime<T, K, N>(shares, count);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Open a fixed list of runtime point shares.
|
||
|
|
/// @tparam T field type
|
||
|
|
/// @tparam K reconstruction threshold
|
||
|
|
/// @tparam N shareholder count
|
||
|
|
/// @tparam M number of shares in the list
|
||
|
|
/// @param shares evaluation points and values
|
||
|
|
/// @return the secret
|
||
|
|
/// @throws std::invalid_argument if the count or the points are illegal
|
||
|
|
/// @throws std::runtime_error if an extra share misses the polynomial of the first K
|
||
|
|
template <typename T, std::size_t K, std::size_t N, std::size_t M>
|
||
|
|
HEDLEY_WARN_UNUSED_RESULT
|
||
|
|
T reconstruct(const std::array<point_share<T>, M> & shares)
|
||
|
|
{
|
||
|
|
return detail::open_runtime<T, K, N>(shares.data(), shares.size());
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace shamir
|
||
|
|
} // namespace dpf
|
||
|
|
|
||
|
|
#endif // LIBDPF_INCLUDE_DPF_SHAMIR_HPP__
|