libdpf/test/tests/secret_share_test.cpp

516 lines
19 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include <array>
#include <cstdint>
#include <cstring>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "dpf.hpp"
namespace
{
struct dpf3_party_tag
{
static constexpr bool is_dpf3 = true;
static constexpr int party = 3;
};
TEST(SecretShare, LayoutMatchesValueType)
{
using T = std::uint64_t;
EXPECT_EQ(sizeof(dpf::additive_share<T, 0>), sizeof(T));
EXPECT_EQ(sizeof(dpf::subtractive_share<T, 1>), sizeof(T));
EXPECT_TRUE((std::is_trivially_copyable_v<dpf::additive_share<T, 0>>));
EXPECT_TRUE((std::is_standard_layout_v<dpf::subtractive_share<T, 1>>));
}
TEST(SecretShare, PlaintextSplitOpens)
{
const std::uint32_t secret = 0xdeadbeefu;
auto [a0, a1] = dpf::make_additive_shares(secret);
EXPECT_EQ(dpf::reconstruct(a0, a1), secret);
EXPECT_EQ(a1.raw(), 0u);
auto [s0, s1] = dpf::make_subtractive_shares(secret);
EXPECT_EQ(dpf::reconstruct(s0, s1), secret);
EXPECT_EQ(s1.raw(), 0u);
}
TEST(SecretShare, SameSchemeLinearCombo)
{
auto [a0, a1] = dpf::make_additive_shares(std::uint32_t{10});
auto [b0, b1] = dpf::make_additive_shares(std::uint32_t{3});
auto c0 = a0 * 2 + b0;
auto c1 = a1 * 2 + b1;
EXPECT_EQ(dpf::reconstruct(c0, c1), 23u);
auto [s0, s1] = dpf::make_subtractive_shares(std::uint32_t{10});
auto [t0, t1] = dpf::make_subtractive_shares(std::uint32_t{3});
auto u0 = s0 * 2 - t0;
auto u1 = s1 * 2 - t1;
EXPECT_EQ(dpf::reconstruct(u0, u1), 17u);
}
TEST(SecretShare, CrossSchemeSigns)
{
auto [a0, a1] = dpf::make_additive_shares(std::int32_t{20});
auto [s0, s1] = dpf::make_subtractive_shares(std::int32_t{7});
// party 0: raw add; party 1: flip the differing-scheme operand
auto r0 = a0 + s0;
auto r1 = a1 + s1;
EXPECT_EQ(dpf::reconstruct(r0, r1), 27);
auto q0 = s0 + a0;
auto q1 = s1 + a1;
EXPECT_EQ(dpf::reconstruct(q0, q1), 27);
}
TEST(SecretShare, PlaintextAbsorbOnParty0)
{
auto [s0, s1] = dpf::make_subtractive_shares(std::uint32_t{5});
s0 += std::uint32_t{10};
s1 += std::uint32_t{10}; // no-op for party 1
EXPECT_EQ(dpf::reconstruct(s0, s1), 15u);
}
TEST(SecretShare, AsAdditivePreservesSecret)
{
auto [s0, s1] = dpf::make_subtractive_shares(std::int32_t{42});
auto a0 = s0.as_additive();
auto a1 = s1.as_additive();
EXPECT_EQ(dpf::reconstruct(a0, a1), 42);
}
TEST(SecretShare, DpfLeafRoundTrip)
{
const std::uint8_t alpha = 0x2a;
const std::uint32_t beta = 0x01020304;
auto [k0, k1] = dpf::make_dpf(alpha, beta);
EXPECT_TRUE(dpf::is_party_key_v<decltype(k0)>);
EXPECT_EQ(decltype(k0)::party, 0u);
EXPECT_EQ(decltype(k1)::party, 1u);
auto y0 = *dpf::eval_point(k0, alpha);
auto y1 = *dpf::eval_point(k1, alpha);
EXPECT_TRUE((std::is_same_v<decltype(y0), dpf::subtractive_share<std::uint32_t, 0>>));
EXPECT_EQ(dpf::reconstruct(y0, y1), beta);
auto z0 = *dpf::eval_point(k0, static_cast<std::uint8_t>(alpha ^ 1));
auto z1 = *dpf::eval_point(k1, static_cast<std::uint8_t>(alpha ^ 1));
EXPECT_EQ(dpf::reconstruct(z0, z1), 0u);
}
TEST(SecretShare, DpfXorLeafRoundTrip)
{
using X = dpf::xor_wrapper<std::uint32_t>;
const std::uint8_t alpha = 7;
const X beta{0xA5A5A5A5u};
auto [k0, k1] = dpf::make_dpf(alpha, beta);
auto y0 = *dpf::eval_point(k0, alpha);
auto y1 = *dpf::eval_point(k1, alpha);
EXPECT_EQ(dpf::reconstruct(y0, y1), beta);
}
TEST(SecretShare, CmpAdditiveRoundTrip)
{
const std::uint32_t alpha = 100u;
const std::uint64_t yt = 5u, yf = 9u;
auto [k0, k1] = dpf::make_dpf(alpha, dpf::lt(yt, yf));
const std::uint64_t mask = k0.cmp().mask;
auto below = dpf::reconstruct(
dpf::eval_point(dpf::cmp, k0, 50u),
dpf::eval_point(dpf::cmp, k1, 50u)) & mask;
auto at = dpf::reconstruct(
dpf::eval_point(dpf::cmp, k0, alpha),
dpf::eval_point(dpf::cmp, k1, alpha)) & mask;
EXPECT_EQ(below, yt);
EXPECT_EQ(at, yf);
}
TEST(SecretShare, PrgExpandSubtractive)
{
using prg = dpf::prg::aes128;
const auto seed0 = dpf::uniform_sample<prg::block_type>();
const auto seed1 = dpf::uniform_sample<prg::block_type>();
auto s0 = prg::expand<std::uint64_t, 0>(seed0, 0);
auto s1 = prg::expand<std::uint64_t, 1>(seed1, 0);
// Same pos, different seeds: reconstruct is raw0 - raw1 (bit pattern).
EXPECT_EQ(dpf::reconstruct(s0, s1),
static_cast<std::uint64_t>(s0.raw() - s1.raw()));
// Same seed → same bits → reconstruct 0
auto t0 = prg::expand<std::uint32_t, 0>(seed0, 3);
auto t1 = prg::expand<std::uint32_t, 1>(seed0, 3);
EXPECT_EQ(dpf::reconstruct(t0, t1), 0u);
}
TEST(SecretShare, Additive3LayoutAndPlaintextSplit)
{
using T = std::uint64_t;
static_assert(dpf::sharing_parties_v<dpf::sharing::additive3> == 3);
static_assert(dpf::sharing_threshold_v<dpf::sharing::additive3> == 3);
static_assert(dpf::is_secret_share_v<dpf::additive3_share<T, 2>>);
static_assert(dpf::share_party_v<dpf::additive3_share<T, 2>> == 2u);
static_assert(dpf::share_scheme_v<dpf::additive3_share<T, 1>>
== dpf::sharing::additive3);
EXPECT_EQ(sizeof(dpf::additive3_share<T, 2>), sizeof(T));
EXPECT_TRUE((std::is_trivially_copyable_v<dpf::additive3_share<T, 2>>));
EXPECT_TRUE((std::is_standard_layout_v<dpf::additive3_share<T, 0>>));
constexpr auto split = dpf::make_additive3_shares(std::uint32_t{9});
static_assert(dpf::reconstruct(
std::get<0>(split), std::get<1>(split), std::get<2>(split)) == 9u);
const std::uint32_t secret = 0xdeadbeefu;
auto [a0, a1, a2] = dpf::make_additive3_shares(secret);
EXPECT_EQ(a1.raw(), 0u);
EXPECT_EQ(a2.raw(), 0u);
EXPECT_EQ(dpf::reconstruct(a2, a0, a1), secret);
}
TEST(SecretShare, Additive3LinearComboAndAbsorb)
{
auto [a0, a1, a2] = dpf::make_additive3_shares(std::uint32_t{10});
auto [b0, b1, b2] = dpf::make_additive3_shares(std::uint32_t{3});
auto c0 = a0 * 2 + b0;
auto c1 = a1 * 2 + b1;
auto c2 = a2 * 2 + b2;
EXPECT_EQ(dpf::reconstruct(c0, c1, c2), 23u);
a0 += std::uint32_t{10};
a1 += std::uint32_t{10};
a2 += std::uint32_t{10};
EXPECT_EQ(dpf::reconstruct(a0, a1, a2), 20u);
const auto raw = std::numeric_limits<std::int32_t>::min();
auto s0 = dpf::additive3_share<std::int32_t, 0>::from_raw(raw);
auto s1 = dpf::additive3_share<std::int32_t, 1>::from_raw(-1);
auto s2 = dpf::additive3_share<std::int32_t, 2>::from_raw(1);
EXPECT_EQ(dpf::reconstruct(s0, s1, s2), raw);
}
TEST(SecretShare, Additive3RandomAndXor)
{
using X = dpf::xor_wrapper<std::uint32_t>;
const X secret{0xA5A5A5A5u};
auto [x0, x1, x2] = dpf::additively_share3(secret);
EXPECT_EQ(dpf::reconstruct(x0, x1, x2), secret);
const std::int32_t n = -42;
auto [a, b, c] = dpf::additively_share3(n);
EXPECT_EQ(dpf::reconstruct(c, b, a), n);
const float f = 1.5f;
auto [f0, f1, f2] = dpf::additively_share3(f);
EXPECT_EQ(dpf::reconstruct(f0, f1, f2), f);
}
TEST(SecretShare, ReplicatedLayoutPlaintextAndPairs)
{
using T = std::uint32_t;
static_assert(dpf::sharing_parties_v<dpf::sharing::replicated> == 3);
static_assert(dpf::sharing_threshold_v<dpf::sharing::replicated> == 2);
static_assert(dpf::share_scheme_v<dpf::replicated_share<T, 2>>
== dpf::sharing::replicated);
EXPECT_EQ(sizeof(dpf::replicated_share<T, 0>), 2 * sizeof(T));
EXPECT_TRUE((std::is_trivially_copyable_v<dpf::replicated_share<T, 1>>));
EXPECT_TRUE((std::is_standard_layout_v<dpf::replicated_share<T, 2>>));
constexpr auto split = dpf::make_replicated_shares(T{11});
static_assert(dpf::reconstruct(std::get<0>(split), std::get<1>(split)) == 11u);
static_assert(dpf::reconstruct(std::get<1>(split), std::get<2>(split)) == 11u);
static_assert(dpf::reconstruct(std::get<2>(split), std::get<0>(split)) == 11u);
auto [r0, r1, r2] = dpf::make_replicated_shares(T{11});
EXPECT_EQ(r0.own, 11u);
EXPECT_EQ(r0.next, 0u);
EXPECT_EQ(r1.own, 0u);
EXPECT_EQ(r1.next, 0u);
EXPECT_EQ(r2.own, 0u);
EXPECT_EQ(r2.next, 11u);
EXPECT_EQ(dpf::reconstruct(r0, r1, r2), 11u);
EXPECT_EQ(r0.as_additive3().raw(), 11u);
EXPECT_EQ(r0.next_additive3().raw(), r1.as_additive3().raw());
}
TEST(SecretShare, ReplicatedLinearComboAbsorbAndAdditive3)
{
auto [a0, a1, a2] = dpf::make_replicated_shares(std::uint32_t{10});
auto [b0, b1, b2] = dpf::make_replicated_shares(std::uint32_t{3});
auto c0 = a0 * 2 - b0;
auto c1 = a1 * 2 - b1;
auto c2 = a2 * 2 - b2;
EXPECT_EQ(dpf::reconstruct(c0, c1), 17u);
EXPECT_EQ(dpf::reconstruct(c1, c2), 17u);
EXPECT_EQ(dpf::reconstruct(c0, c2, c1), 17u);
c0 += std::uint32_t{4};
c1 += std::uint32_t{4};
c2 += std::uint32_t{4};
EXPECT_EQ(c1.own, (a1 * 2 - b1).own);
EXPECT_EQ(c1.next, (a1 * 2 - b1).next);
EXPECT_EQ(dpf::reconstruct(c2, c0), 21u);
auto [d0, d1, d2] = dpf::make_additive3_shares(std::uint32_t{1});
d1 = dpf::additive3_share<std::uint32_t, 1>::from_raw(2u);
d2 = dpf::additive3_share<std::uint32_t, 2>::from_raw(4u);
auto [e0, e1, e2] = dpf::add_replicated(c0, c1, c2, d0, d1, d2);
EXPECT_EQ(e0.next, e1.own);
EXPECT_EQ(e1.next, e2.own);
EXPECT_EQ(e2.next, e0.own);
EXPECT_EQ(dpf::reconstruct(e0, e1), 28u);
EXPECT_EQ(dpf::reconstruct(e1, e2, e0), dpf::reconstruct(e0, e2));
}
TEST(SecretShare, ReplicatedFromComponentsAndRandom)
{
using X = dpf::xor_wrapper<std::uint16_t>;
const X secret{0xBEEFu};
auto [a0, a1, a2] = dpf::additively_share3(secret);
auto [r0, r1, r2] = dpf::make_replicated_shares(a0, a1, a2);
EXPECT_EQ(r0.own, a0.raw());
EXPECT_EQ(r0.next, a1.raw());
EXPECT_EQ(r2.next, a0.raw());
EXPECT_EQ(dpf::reconstruct(r2, r1), secret);
EXPECT_EQ(dpf::reconstruct(r0.as_additive3(), r1.as_additive3(),
r2.as_additive3()), secret);
auto built = dpf::replicated_share<X, 0>::from_additive3(
a0, a1);
EXPECT_EQ(built, r0);
const std::int64_t n = std::numeric_limits<std::int64_t>::min() + 7;
auto [s0, s1, s2] = dpf::share_replicated(n);
EXPECT_EQ(s0.next, s1.own);
EXPECT_EQ(s1.next, s2.own);
EXPECT_EQ(s2.next, s0.own);
EXPECT_EQ(dpf::reconstruct(s0, s1), n);
EXPECT_EQ(dpf::reconstruct(s1, s2), n);
EXPECT_EQ(dpf::reconstruct(s2, s0), n);
EXPECT_EQ(dpf::reconstruct(s2, s1, s0), n);
auto [p0, p1, p2] = dpf::make_replicated_shares(std::uint32_t{8});
dpf::replicated_share<std::uint32_t, 0> slot{};
dpf::assign_share_slot(slot, p0);
EXPECT_EQ(slot, p0);
std::uint32_t word = 0;
dpf::assign_share_slot(word, p0.as_additive3());
EXPECT_EQ(word, 8u);
(void)p1;
(void)p2;
}
TEST(SecretShare, TwoPartyConversionsPreserveSecret)
{
auto a0 = dpf::additive_share<std::int32_t, 0>::from_raw(10);
auto a1 = dpf::additive_share<std::int32_t, 1>::from_raw(5);
auto b0 = dpf::a2b(a0);
auto b1 = dpf::a2b(a1);
EXPECT_EQ(b0.raw(), 10);
EXPECT_EQ(b1.raw(), -5);
EXPECT_EQ(dpf::reconstruct(b0, b1), 15);
EXPECT_EQ(dpf::reconstruct(dpf::b2a(b0), dpf::b2a(b1)), 15);
auto f0 = dpf::a2fss(a0);
auto f1 = dpf::a2fss(a1);
EXPECT_EQ(f1.raw(), -5);
EXPECT_EQ(dpf::reconstruct(dpf::fss2a(f0), dpf::fss2a(f1)), 15);
EXPECT_EQ(dpf::fss2b(f1).raw(), f1.raw());
EXPECT_EQ(dpf::b2fss(b1).raw(), b1.raw());
// Subtractive and FSS share a sign, so party 1 adds the raw words.
auto mixed = b1 + f1;
EXPECT_EQ(mixed.raw(), -10);
// Additive and FSS disagree on party 1, so the FSS word is flipped.
auto flipped = a1 + dpf::fss_share<std::int32_t, 1>::from_raw(4);
EXPECT_EQ(flipped.raw(), 1);
}
TEST(SecretShare, ShamirRoundTripAndReplicatedProduct)
{
const dpf::fp61 secret{20};
const dpf::fp61 slope{3};
auto [s0, s1, s2] = dpf::make_shamir_shares(secret, slope);
EXPECT_EQ((dpf::shamir_share<dpf::fp61, 0>::point), 1u);
EXPECT_EQ(s0.raw(), secret + slope * dpf::fp61{1});
EXPECT_EQ(s1.raw(), secret + slope * dpf::fp61{2});
EXPECT_EQ(s2.raw(), secret + slope * dpf::fp61{3});
EXPECT_EQ(dpf::reconstruct(s0, s1), secret);
EXPECT_EQ(dpf::reconstruct(s1, s2), secret);
EXPECT_EQ(dpf::reconstruct(s2, s0, s1), secret);
s0 += dpf::fp61{4};
s1 += dpf::fp61{4};
s2 += dpf::fp61{4};
EXPECT_EQ(dpf::reconstruct(s2, s0), secret + dpf::fp61{4});
auto [y0, y1, y2] = dpf::s2y(s0, s1);
EXPECT_EQ(dpf::reconstruct(y0, y1, y2), secret + dpf::fp61{4});
auto back = dpf::y2s(y0, y1, y2, slope);
EXPECT_EQ(dpf::reconstruct(std::get<0>(back), std::get<2>(back)),
secret + dpf::fp61{4});
auto [r0, r1, r2] = dpf::s2rss(s0, s2);
EXPECT_EQ(dpf::reconstruct(r0, r1), secret + dpf::fp61{4});
auto shamir_again = dpf::rss2s(r1, r2, slope);
EXPECT_EQ(dpf::reconstruct(std::get<1>(shamir_again), std::get<2>(shamir_again)),
secret + dpf::fp61{4});
auto tagged = dpf::as_shamir_share(dpf3_party_tag{}, dpf::fp61{7});
EXPECT_EQ(decltype(tagged)::party, 2u);
EXPECT_EQ(tagged.raw(), dpf::fp61{7});
auto runtime = dpf::shamir3::runtime_share(s0);
EXPECT_EQ(runtime.party, 1);
EXPECT_EQ(dpf::shamir3::typed_share<0>(runtime), s0);
EXPECT_EQ(dpf::shamir3::reconstruct(runtime,
dpf::shamir3::runtime_share(s1)), secret + dpf::fp61{4});
auto [x0, x1, x2] = dpf::make_replicated_shares(
std::uint32_t{3}, std::uint32_t{5}, std::uint32_t{1});
auto [z0, z1, z2] = dpf::make_replicated_shares(
std::uint32_t{4}, std::uint32_t{2}, std::uint32_t{6});
EXPECT_EQ(dpf::reconstruct(dpf::rss_mul(x0, z0), dpf::rss_mul(x1, z1),
dpf::rss_mul(x2, z2)), 108u);
auto [p0, p1, p2] = dpf::rss_mul(x0, x1, x2, z0, z1, z2);
EXPECT_EQ(p0.next, p1.own);
EXPECT_EQ(dpf::reconstruct(p0, p2), 108u);
auto m0 = dpf::additive3_share<std::uint32_t, 0>::from_raw(9u);
auto m1 = dpf::additive3_share<std::uint32_t, 1>::from_raw(1u);
auto m2 = dpf::additive3_share<std::uint32_t, 2>::from_raw(
static_cast<std::uint32_t>(0u - 9u - 1u));
auto [q0, q1, q2] = dpf::rss_mul(x0, x1, x2, z0, z1, z2, m0, m1, m2);
EXPECT_EQ(dpf::reconstruct(q1, q0), 108u);
EXPECT_NE(q0.own, p0.own);
(void)q2;
auto [h0, h1, h2] = dpf::y2rss(dpf::rss2y(p0), dpf::rss2y(p1), dpf::rss2y(p2));
EXPECT_EQ(h0, p0);
EXPECT_EQ(h1, p1);
EXPECT_EQ(h2, p2);
}
TEST(SecretShare, ShamirKnSpecializesTwoOfThree)
{
using F = dpf::fp61;
static_assert(std::is_same_v<
dpf::shamir::share<F, 0, 2, 3>, dpf::shamir_share<F, 0>>);
static_assert(std::is_same_v<
dpf::shamir::share<F, 2, 2, 3>, dpf::shamir_share<F, 2>>);
static_assert(dpf::shamir::two_of_three::threshold == 2);
static_assert(dpf::shamir::two_of_three::parties == 3);
static_assert(dpf::shamir::two_of_three::degree == 1);
static_assert(dpf::shamir_share<F, 1>::threshold == 2);
static_assert(dpf::shamir_share<F, 1>::parties == 3);
static_assert(!dpf::is_secret_share_v<dpf::shamir::share<F, 0, 3, 5>>);
static_assert(dpf::shamir::is_share_v<dpf::shamir::share<F, 0, 3, 5>>);
static_assert(dpf::shamir::is_share_v<dpf::shamir_share<F, 0>>);
EXPECT_EQ(sizeof(dpf::shamir::share<F, 4, 3, 5>), sizeof(F));
EXPECT_TRUE((std::is_trivially_copyable_v<dpf::shamir::share<F, 4, 3, 5>>));
EXPECT_TRUE((std::is_standard_layout_v<dpf::shamir_share<F, 0>>));
// p(x) = 10 + 2x + 3x^2. Points 1..5 are 15, 26, 43, 66, 95.
const F secret{10};
const std::array<F, 2> coeff{{F{2}, F{3}}};
const F expect[5] = {F{15}, F{26}, F{43}, F{66}, F{95}};
auto dealt = dpf::make_shamir_shares<3, 5>(secret, coeff);
const std::array<F, 5> raw{{
std::get<0>(dealt).raw(), std::get<1>(dealt).raw(),
std::get<2>(dealt).raw(), std::get<3>(dealt).raw(),
std::get<4>(dealt).raw()}};
for (int i = 0; i < 5; ++i)
EXPECT_EQ(raw[static_cast<std::size_t>(i)], expect[i]);
EXPECT_EQ(std::get<4>(dealt).point, 5u);
EXPECT_EQ(std::get<4>(dealt).party, 4u);
for (int i = 0; i < 5; ++i)
for (int j = i + 1; j < 5; ++j)
for (int k = j + 1; k < 5; ++k)
{
const std::array<dpf::shamir::point_share<F>, 3> subset{{
{i + 1, raw[static_cast<std::size_t>(i)]},
{j + 1, raw[static_cast<std::size_t>(j)]},
{k + 1, raw[static_cast<std::size_t>(k)]}}};
const F opened = dpf::shamir::reconstruct<F, 3, 5>(subset);
EXPECT_EQ(opened, secret);
}
EXPECT_EQ(dpf::shamir::reconstruct(
std::get<0>(dealt), std::get<2>(dealt), std::get<4>(dealt)), secret);
auto scaled0 = std::get<0>(dealt) * F{3};
auto scaled1 = std::get<1>(dealt) * F{3};
auto scaled2 = std::get<2>(dealt) * F{3};
EXPECT_EQ(dpf::shamir::reconstruct(scaled0, scaled1, scaled2), secret * F{3});
auto sum0 = std::get<0>(dealt) + std::get<0>(dealt);
auto sum1 = std::get<1>(dealt) + std::get<1>(dealt);
auto sum2 = std::get<2>(dealt) + std::get<2>(dealt);
EXPECT_EQ(dpf::shamir::reconstruct(sum0, sum1, sum2), secret + secret);
std::get<0>(dealt) += F{4};
std::get<1>(dealt) += F{4};
std::get<2>(dealt) += F{4};
EXPECT_EQ(dpf::shamir::reconstruct(
std::get<0>(dealt), std::get<1>(dealt), std::get<2>(dealt)),
secret + F{4});
std::array<dpf::shamir::point_share<F>, 4> extra{{
{1, raw[0]}, {2, raw[1]}, {3, raw[2]}, {4, raw[3]}}};
EXPECT_EQ((dpf::shamir::reconstruct<F, 3, 5>(extra)), secret);
extra[3].value = extra[3].value + F{1};
EXPECT_THROW((dpf::shamir::reconstruct<F, 3, 5>(extra)), std::runtime_error);
const std::array<dpf::shamir::point_share<F>, 2> too_few{{
{1, raw[0]}, {2, raw[1]}}};
EXPECT_THROW((dpf::shamir::reconstruct<F, 3, 5>(too_few)),
std::invalid_argument);
const std::array<dpf::shamir::point_share<F>, 2> dup{{
{1, raw[0]}, {1, raw[0]}}};
EXPECT_THROW((dpf::shamir::reconstruct<F, 2, 5>(dup)), std::invalid_argument);
// Threshold 2 is not tied to three shareholders. p(x) = 8 + 5x.
const auto line = dpf::shamir::deal<F, 2, 4>(F{8}, std::array<F, 1>{{F{5}}});
EXPECT_EQ(std::get<0>(line).raw(), F{13});
EXPECT_EQ(std::get<3>(line).raw(), F{28});
EXPECT_EQ(dpf::shamir::reconstruct(std::get<1>(line), std::get<3>(line)), F{8});
// Threshold 1 copies the secret. Threshold N needs every share.
const auto copied = dpf::shamir::deal<F, 1, 3>(F{9}, std::array<F, 0>{});
EXPECT_EQ(std::get<0>(copied).raw(), F{9});
EXPECT_EQ(std::get<2>(copied).raw(), F{9});
EXPECT_EQ(dpf::shamir::reconstruct(std::get<1>(copied)), F{9});
const auto plain = dpf::shamir::share_secret<F, 1, 3>(F{6});
EXPECT_EQ(std::get<0>(plain).raw(), F{6});
EXPECT_EQ(std::get<2>(plain).raw(), F{6});
const std::array<F, 3> dense{{F{1}, F{0}, F{4}}};
const auto all = dpf::shamir::deal<F, 4, 4>(F{7}, dense);
EXPECT_EQ(dpf::shamir::reconstruct(std::get<0>(all), std::get<1>(all),
std::get<2>(all), std::get<3>(all)), F{7});
const std::array<dpf::shamir::point_share<F>, 3> missing{{
{1, std::get<0>(all).raw()},
{2, std::get<1>(all).raw()},
{3, std::get<2>(all).raw()}}};
EXPECT_THROW((dpf::shamir::reconstruct<F, 4, 4>(missing)),
std::invalid_argument);
const auto rnd = dpf::shamir::share_secret<F, 2, 3>(F{11});
static_assert(std::is_same_v<std::decay_t<decltype(std::get<0>(rnd))>,
dpf::shamir_share<F, 0>>);
EXPECT_EQ(dpf::reconstruct(std::get<0>(rnd), std::get<2>(rnd)), F{11});
const auto wider = dpf::shamir::share_secret<F, 3, 5>(F{12});
EXPECT_EQ(dpf::shamir::reconstruct(
std::get<0>(wider), std::get<2>(wider), std::get<4>(wider)), F{12});
}
} // namespace