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>
515 lines
19 KiB
C++
515 lines
19 KiB
C++
#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
|