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

@ -5,8 +5,11 @@
#include <tuple>
#include <vector>
#include <cstring>
#include "dpf/beaver.hpp"
#include "dpf/buffered_prg.hpp"
#include "dpf/doerner_shelat.hpp"
#include "dpf/modint.hpp"
namespace
@ -303,6 +306,37 @@ TEST(Beaver, InnerProductMatchesTheSumAndIsOneCross)
EXPECT_EQ(fused.open(again), expect);
}
TEST(Beaver, ScheduleObjectiveRoundsKeepsOneRound)
{
session64 latency;
latency.set_schedule_objective(dpf::beavers::schedule_objective::rounds);
auto sgn = latency.input();
auto x = latency.input();
auto a0 = latency.input();
auto a1 = latency.input();
auto lin = latency(sgn * (a1 * x + a0));
EXPECT_EQ(latency.round_of(lin), 1);
session64 prep;
prep.set_schedule_objective(dpf::beavers::schedule_objective::prep);
auto ps = prep.input();
auto px = prep.input();
auto pa0 = prep.input();
auto pa1 = prep.input();
auto plin = prep(ps * (pa1 * px + pa0));
EXPECT_EQ(prep.round_of(plin), 2);
EXPECT_LT(prep.preprocessing_count(), latency.preprocessing_count());
Counter rng;
latency.sample(rng);
latency.bind(sgn, u64{3}, rng);
latency.bind(x, u64{2}, rng);
latency.bind(a0, u64{4}, rng);
latency.bind(a1, u64{5}, rng);
latency.evaluate();
EXPECT_EQ(latency.open(lin), 3u * (5u * 2u + 4u));
}
TEST(Beaver, InnerProductPeelsASharedFactor)
{
session64 s;
@ -632,16 +666,16 @@ TEST(Beaver, InnerProductRejectsABadShape)
auto x = s.input();
auto y = s.input();
auto z = s.input();
EXPECT_THROW((void)[&] {
EXPECT_THROW([&] {
return s.dot(std::initializer_list<wire64>{}, std::initializer_list<wire64>{});
}(), std::invalid_argument);
EXPECT_THROW((void)[&] { return s.dot({x}, {y, z}); }(), std::invalid_argument);
EXPECT_THROW([&] { return s.dot({x}, {y, z}); }(), std::invalid_argument);
session64 other;
auto w = other.input();
EXPECT_THROW((void)[&] { return s.dot({x}, {w}); }(), std::invalid_argument);
EXPECT_THROW([&] { return s.dot({x}, {w}); }(), std::invalid_argument);
auto dotted = s.dot({x}, {y});
EXPECT_THROW((void)[&] { return s.dot_cross(dotted); }(), std::logic_error);
EXPECT_THROW((void)[&] { return s.dot_cross(x); }(), std::invalid_argument);
EXPECT_THROW([&] { return s.dot_cross(dotted); }(), std::logic_error);
EXPECT_THROW([&] { return s.dot_cross(x); }(), std::invalid_argument);
}
TEST(Beaver, ScaleSharesScalarBlind)
@ -1531,7 +1565,7 @@ TEST(Beaver, FactoredProductIsVisibleAndTheSexticTermIsNot)
EXPECT_EQ(s.preprocessing_count(), prep);
EXPECT_EQ(s.monomial({{x, 1u}, {z, 1u}}).open(),
s.lambda(x).open() * s.lambda(z).open());
EXPECT_THROW((void)[&] { return s.monomial({{x, 2u}, {z, 2u}}); }(),
EXPECT_THROW([&] { return s.monomial({{x, 2u}, {z, 2u}}); }(),
std::logic_error);
s.bind(x, u64{6}, rng);
s.bind(z, u64{7}, rng);
@ -1909,8 +1943,8 @@ TEST(Beaver, HighPowersAndTheExpansionLimit)
session64 s;
auto x = s.input();
EXPECT_THROW((void)[&] { return pow(x, 17u); }(), std::invalid_argument);
EXPECT_THROW((void)[&] { return pow(x, 10) * pow(x, 7); }(), std::invalid_argument);
EXPECT_THROW([&] { return pow(x, 17u); }(), std::invalid_argument);
EXPECT_THROW([&] { return pow(x, 10) * pow(x, 7); }(), std::invalid_argument);
std::vector<wire64> wide;
wide.reserve(12);
@ -1929,7 +1963,7 @@ TEST(Beaver, HighPowersAndTheExpansionLimit)
EXPECT_EQ(s.open(all), 1u);
auto thirteenth = s.input();
EXPECT_THROW((void)[&] { return s(expr12 * thirteenth); }(), std::invalid_argument);
EXPECT_THROW([&] { return s(expr12 * thirteenth); }(), std::invalid_argument);
}
TEST(Beaver, ScheduledPolynomialRejectsEarlyUse)
@ -1938,8 +1972,8 @@ TEST(Beaver, ScheduledPolynomialRejectsEarlyUse)
session64 b;
auto x = a.input();
auto y = b.input();
EXPECT_THROW((void)[&] { return x + y; }(), std::invalid_argument);
EXPECT_THROW((void)[&] { return x * y; }(), std::invalid_argument);
EXPECT_THROW([&] { return x + y; }(), std::invalid_argument);
EXPECT_THROW([&] { return x * y; }(), std::invalid_argument);
auto z = a(x + pow(x, 2));
EXPECT_THROW(a.open(z), std::logic_error);
EXPECT_THROW(a.evaluate(), std::logic_error);
@ -1959,15 +1993,15 @@ TEST(Beaver, RejectsBadUse)
dpf::beavers::session<u64> b;
auto x = a.input();
auto y = b.input();
EXPECT_THROW((void)[&] { return a.product(x, y); }(), std::invalid_argument);
EXPECT_THROW((void)[&] { return x * y; }(), std::invalid_argument);
EXPECT_THROW([&] { return a.product(x, y); }(), std::invalid_argument);
EXPECT_THROW([&] { return x * y; }(), std::invalid_argument);
auto bit = a.bit();
EXPECT_THROW(a.bind(bit, u64{2}), std::invalid_argument);
auto z = a.product(x, x);
EXPECT_THROW(a.evaluate(), std::logic_error);
a.sample();
EXPECT_THROW(a.evaluate(), std::logic_error);
EXPECT_THROW((void)[&] { return a.bit_mul(x, x); }(), std::invalid_argument);
EXPECT_THROW([&] { return a.bit_mul(x, x); }(), std::invalid_argument);
(void)z;
}
@ -1993,3 +2027,178 @@ TEST(Beaver, ProductExtremes)
EXPECT_EQ(s.open(z), want) << a << " * " << b;
}
}
TEST(BeaverAuth, AuthBeaver2HonestAndTamper)
{
auto key = dpf::sample_mac_key<u64>();
auto t = dpf::beavers::sample_auth_beaver2<u64>(key);
EXPECT_TRUE(t.verify(key));
EXPECT_EQ(t.ab.open(), t.a.open() * t.b.open());
auto bad = t;
bad.ab.tag.p0 ^= 1ull;
EXPECT_FALSE(bad.verify(key));
}
TEST(BeaverAuth, AuthBeaverMulMatchesProduct)
{
auto key = dpf::sample_mac_key<u64>();
auto bev = dpf::beavers::sample_auth_beaver2<u64>(key);
const u64 x = 7, y = 11;
auto xs = dpf::beavers::auth_share(x, key);
auto ys = dpf::beavers::auth_share(y, key);
auto [z0, z1] = dpf::beavers::auth_beaver_mul(
xs.party(0), xs.party(1), ys.party(0), ys.party(1), bev, key);
EXPECT_EQ(z0.value + z1.value, x * y);
EXPECT_TRUE(dpf::mac_verify(z0, z1, key));
z0.tag ^= 1ull;
EXPECT_FALSE(dpf::mac_verify(z0, z1, key));
}
TEST(BeaverAuth, SessionAby2ValuesVerify)
{
auto key = dpf::sample_mac_key<u64>();
session64 s;
s.set_mac_key(key);
auto x = s.input();
auto y = s.input();
auto z = s(x * y);
Counter rng;
s.sample(rng);
s.bind(x, u64{6}, rng);
s.bind(y, u64{7}, rng);
s.evaluate();
EXPECT_EQ(s.open(z), 42u);
EXPECT_TRUE(s.lambda_auth(x).verify(key));
EXPECT_TRUE(s.value_auth(x).verify(key));
EXPECT_TRUE(s.value_auth(z).verify(key));
EXPECT_TRUE(s.verify_delta(x));
EXPECT_TRUE(s.verify_delta(y));
EXPECT_TRUE(s.verify_all());
auto tampered = s.value_auth(z);
tampered.tag.p0 ^= 1ull;
EXPECT_FALSE(tampered.verify(key));
}
TEST(BeaverAuth, PartyTapeCarriesTagsAndOpeningsCheck)
{
auto key = dpf::sample_mac_key<u64>();
session64 dealer;
dealer.set_mac_key(key);
auto x = dealer.input();
auto y = dealer.input();
auto z = dealer(x * y);
Counter rng;
dealer.sample(rng);
auto tape0 = dealer.export_party(0);
auto tape1 = dealer.export_party(1);
EXPECT_TRUE(tape0.has_mac);
EXPECT_TRUE(tape1.has_mac);
EXPECT_EQ(tape0.lambda_tag.size(), tape0.lambda.size());
EXPECT_EQ(tape0.lambda[0] + tape1.lambda[0], dealer.lambda(x).open());
EXPECT_EQ(tape0.lambda_tag[0] + tape1.lambda_tag[0],
dealer.lambda_auth(x).tag.open());
dealer.bind(x, u64{3}, rng);
dealer.bind(y, u64{5}, rng);
dealer.evaluate();
EXPECT_EQ(dealer.open(z), 15u);
EXPECT_TRUE(dealer.verify_all());
auto honest = dealer.delta_auth(x);
dpf::beavers::auth_opening<u64> a{honest.value.p0, honest.tag.p0};
dpf::beavers::auth_opening<u64> b{honest.value.p1, honest.tag.p1};
EXPECT_TRUE(dpf::beavers::verify_auth_opening(a, b, key));
b.value ^= 1ull;
EXPECT_FALSE(dpf::beavers::verify_auth_opening(a, b, key));
// Party views: authenticated input shares + λ tags reconstruct δ.
auto xv = dealer.value_auth(x);
dpf::beavers::auth_opening<u64> ox0{
xv.party(0).value + tape0.lambda[0],
xv.party(0).tag + tape0.lambda_tag[0]};
dpf::beavers::auth_opening<u64> ox1{
xv.party(1).value + tape1.lambda[0],
xv.party(1).tag + tape1.lambda_tag[0]};
EXPECT_TRUE(dpf::beavers::verify_auth_opening(ox0, ox1, key));
EXPECT_EQ(ox0.value + ox1.value, dealer.delta(x));
}
TEST(Beaver, OracleAuthTagsReplayFromTheSeed)
{
using block = dpf::prg::aes128::block_type;
const block seed = simde_mm_set_epi64x(0x51, 0x52);
auto key = dpf::sample_mac_key<u64>();
dpf::beavers::oracle<u64> left(seed, 4);
dpf::beavers::oracle<u64> right(seed, 4);
auto a = dpf::beavers::sample_auth_beaver2(key, left, 3);
auto b = dpf::beavers::sample_auth_beaver2(key, right, 3);
EXPECT_EQ(a.a, b.a);
EXPECT_EQ(a.b, b.b);
EXPECT_EQ(a.ab, b.ab);
EXPECT_EQ(a.out, b.out);
EXPECT_TRUE(a.verify(key));
EXPECT_EQ(a.ab.open(), a.a.open() * a.b.open());
dpf::beavers::session<u64> session;
session.set_mac_key(key);
auto x = session.input();
auto y = session.input();
auto z = session(x * y);
session.pin(z);
session.sample_from(left, 3);
EXPECT_TRUE(session.lambda_auth(x).verify(key));
EXPECT_TRUE(session.monomial_auth({{x, 1u}, {y, 1u}}).verify(key));
EXPECT_EQ(session.lambda(x), a.a.value);
}
TEST(Beaver, OracleShapesOpen)
{
using block = dpf::prg::aes128::block_type;
const block seed = simde_mm_set_epi64x(0x71, 0x72);
dpf::beavers::oracle<u64> src(seed, 8);
auto p3 = dpf::beavers::sample_beaver3(src, 1);
EXPECT_EQ(p3.abc.open(), p3.a.open() * p3.b.open() * p3.c.open());
auto sq = dpf::beavers::sample_square(src, 2);
EXPECT_EQ(sq.x2.open(), sq.x.open() * sq.x.open());
auto sc = dpf::beavers::sample_scale(2, src, 4);
EXPECT_EQ(sc.cross[1].open(), sc.scalar.open() * sc.lanes[1].open());
auto fresh = dpf::beavers::sample_fresh<3, u64>(src, 5);
EXPECT_EQ(fresh.subset[(1u << 3) - 2].open(),
fresh.in[0].open() * fresh.in[1].open() * fresh.in[2].open());
}
TEST(Beaver, PrgPadReplaysDsGadgets)
{
using block = dpf::prg::aes128::block_type;
const block seed = simde_mm_set_epi64x(0x81, 0x82);
dpf::prg_pad_rng<> left(seed);
dpf::prg_pad_rng<> right(seed);
const auto a = dpf::detail::ds_sample_bit_and(left);
const auto b = dpf::detail::ds_sample_bit_and(right);
EXPECT_EQ(a.a0, b.a0);
EXPECT_EQ(a.c1, b.c1);
const auto abit = static_cast<std::uint8_t>(a.a0 ^ a.a1);
const auto bbit = static_cast<std::uint8_t>(a.b0 ^ a.b1);
const auto cbit = static_cast<std::uint8_t>(a.c0 ^ a.c1);
EXPECT_EQ(cbit, static_cast<std::uint8_t>(abit & bbit));
dpf::prg_pad_rng<> cleft(seed);
dpf::prg_pad_rng<> cright(seed);
const auto cw0 = dpf::detail::ds_sample_cw(cleft);
const auto cw1 = dpf::detail::ds_sample_cw(cright);
EXPECT_EQ(std::memcmp(&cw0.p0.rand, &cw1.p0.rand, sizeof(cw0.p0.rand)), 0);
EXPECT_EQ(std::memcmp(&cw0.p1.gamma, &cw1.p1.gamma, sizeof(cw0.p1.gamma)), 0);
const auto prod = dpf::detail::ds_xor(
(cw0.p1.bit & 1u) ? cw0.p0.rand : simde_mm_setzero_si128(),
(cw0.p0.bit & 1u) ? cw0.p1.rand : simde_mm_setzero_si128());
const auto got = dpf::detail::ds_xor(cw0.p0.gamma, cw0.p1.gamma);
EXPECT_EQ(std::memcmp(&got, &prod, sizeof(got)), 0);
dpf::detail::urandom_pad_rng pad;
const auto shares = dpf::beavers::sample_bit_arith(pad);
EXPECT_EQ(shares.add0 + shares.add1,
static_cast<std::uint64_t>(shares.xor0 ^ shares.xor1));
}