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>
This commit is contained in:
Ryan Henry 2026-09-28 05:59:19 -06:00
parent 695f8e84f7
commit 0d22946a0e
1835 changed files with 170291 additions and 2849 deletions

View file

@ -1,7 +1,10 @@
#include <gtest/gtest.h>
#include <array>
#include <cstdint>
#include <cstring>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <utility>
@ -10,6 +13,14 @@
namespace
{
struct dpf3_party_tag
{
static constexpr bool is_dpf3 = true;
static constexpr int party = 3;
};
TEST(SecretShare, LayoutMatchesValueType)
{
using T = std::uint64_t;
@ -140,4 +151,365 @@ TEST(SecretShare, PrgExpandSubtractive)
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