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:
parent
695f8e84f7
commit
0d22946a0e
1835 changed files with 170291 additions and 2849 deletions
331
test/tests/pprf_test.cpp
Normal file
331
test/tests/pprf_test.cpp
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "dpf.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <typename Block>
|
||||
bool eq_block(const Block & a, const Block & b)
|
||||
{
|
||||
return std::memcmp(&a, &b, sizeof(Block)) == 0;
|
||||
}
|
||||
|
||||
template <typename InputT>
|
||||
void check_pprf_spread(InputT alpha)
|
||||
{
|
||||
auto master = dpf::make_pprf_master<InputT>();
|
||||
auto punctured = dpf::puncture(master, alpha, /*program_alpha=*/true);
|
||||
|
||||
const auto master_at_alpha = dpf::pprf_eval(master, alpha);
|
||||
EXPECT_TRUE(eq_block(master_at_alpha, *punctured.programmed));
|
||||
EXPECT_TRUE(eq_block(dpf::pprf_eval(punctured, alpha), master_at_alpha));
|
||||
|
||||
std::vector<InputT> points = {
|
||||
InputT{0},
|
||||
InputT{1},
|
||||
static_cast<InputT>(alpha + 1),
|
||||
static_cast<InputT>(alpha ^ InputT{1}),
|
||||
};
|
||||
if constexpr (dpf::utils::bitlength_of_v<InputT> > 8)
|
||||
{
|
||||
points.push_back(static_cast<InputT>(
|
||||
InputT{1} << (dpf::utils::bitlength_of_v<InputT> / 2)));
|
||||
points.push_back(static_cast<InputT>(alpha ^ (InputT{1} << 7)));
|
||||
}
|
||||
|
||||
for (InputT x : points)
|
||||
{
|
||||
if (x == alpha)
|
||||
continue;
|
||||
EXPECT_TRUE(eq_block(dpf::pprf_eval(master, x),
|
||||
dpf::pprf_eval(punctured, x)));
|
||||
}
|
||||
|
||||
auto bare = dpf::puncture(master, alpha, /*program_alpha=*/false);
|
||||
EXPECT_FALSE(bare.programmed.has_value());
|
||||
EXPECT_THROW((void)dpf::pprf_eval(bare, alpha), std::invalid_argument);
|
||||
for (InputT x : points)
|
||||
{
|
||||
if (x == alpha)
|
||||
continue;
|
||||
EXPECT_TRUE(eq_block(dpf::pprf_eval(master, x),
|
||||
dpf::pprf_eval(bare, x)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(punctured.siblings.size(), dpf::utils::bitlength_of_v<InputT>);
|
||||
}
|
||||
|
||||
bool node_has_seed(const dpf::pprf_copath<std::uint8_t> & copath,
|
||||
const dpf::prg::aes128::block_type & seed)
|
||||
{
|
||||
for (const auto & node : copath.nodes)
|
||||
{
|
||||
if (eq_block(node.seed, seed))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(Pprf, PathBitHelpersAreConstexpr)
|
||||
{
|
||||
using input_t = std::uint8_t;
|
||||
static_assert(dpf::detail::pprf_impl::path_bit<input_t>(0b1010'0000u, 0));
|
||||
static_assert(!dpf::detail::pprf_impl::path_bit<input_t>(0b0010'0000u, 0));
|
||||
static_assert(dpf::detail::pprf_impl::path_prefix<input_t>(0b1010'1100u, 4)
|
||||
== input_t{0b1010u});
|
||||
static_assert(dpf::detail::pprf_impl::prefix_matches<input_t>(
|
||||
0b1010'1100u, 4, input_t{0b1010u}));
|
||||
static_assert(!dpf::detail::pprf_impl::prefix_matches<input_t>(
|
||||
0b1010'1100u, 4, input_t{0b1011u}));
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST(Pprf, Uint32)
|
||||
{
|
||||
check_pprf_spread<std::uint32_t>(0x00c0ffeeu);
|
||||
}
|
||||
|
||||
TEST(Pprf, Uint128)
|
||||
{
|
||||
using input_t = simde_uint128;
|
||||
const input_t alpha = (input_t{1} << 120) | input_t{0xdeadbeefull};
|
||||
check_pprf_spread<input_t>(alpha);
|
||||
}
|
||||
|
||||
TEST(PprfCopath, OneHiddenAgreesWithOnePoint)
|
||||
{
|
||||
using input_t = std::uint8_t;
|
||||
constexpr std::size_t n = dpf::utils::bitlength_of_v<input_t>;
|
||||
auto master = dpf::make_pprf_master<input_t>();
|
||||
const input_t alpha = 0x2au;
|
||||
|
||||
auto one = dpf::puncture(master, alpha, /*program_alpha=*/true);
|
||||
const input_t hidden[] = {alpha};
|
||||
auto copath = dpf::puncture(master, std::begin(hidden), std::end(hidden),
|
||||
/*program_hidden=*/true);
|
||||
|
||||
ASSERT_EQ(copath.nodes.size(), n);
|
||||
ASSERT_EQ(copath.programmed.size(), 1u);
|
||||
EXPECT_TRUE(eq_block(copath.programmed[0], *one.programmed));
|
||||
|
||||
for (std::size_t i = 0; i < n; ++i)
|
||||
{
|
||||
EXPECT_EQ(copath.nodes[i].level, i + 1);
|
||||
EXPECT_TRUE(eq_block(copath.nodes[i].seed, one.siblings[i]));
|
||||
EXPECT_EQ(copath.nodes[i].prefix,
|
||||
dpf::detail::pprf_impl::path_prefix(
|
||||
static_cast<input_t>(
|
||||
alpha ^ static_cast<input_t>(input_t{1} << (n - 1 - i))),
|
||||
i + 1));
|
||||
}
|
||||
|
||||
for (unsigned x = 0; x < 256; ++x)
|
||||
{
|
||||
const auto xi = static_cast<input_t>(x);
|
||||
EXPECT_TRUE(eq_block(dpf::pprf_eval(copath, xi), dpf::pprf_eval(one, xi)));
|
||||
if (xi != alpha)
|
||||
EXPECT_TRUE(
|
||||
eq_block(dpf::pprf_eval(copath, xi), dpf::pprf_eval(master, xi)));
|
||||
}
|
||||
|
||||
auto bare = dpf::puncture(master, std::begin(hidden), std::end(hidden),
|
||||
/*program_hidden=*/false);
|
||||
EXPECT_TRUE(bare.programmed.empty());
|
||||
EXPECT_THROW((void)dpf::pprf_eval(bare, alpha), std::invalid_argument);
|
||||
for (unsigned x = 0; x < 256; ++x)
|
||||
{
|
||||
const auto xi = static_cast<input_t>(x);
|
||||
if (xi == alpha)
|
||||
continue;
|
||||
EXPECT_TRUE(
|
||||
eq_block(dpf::pprf_eval(bare, xi), dpf::pprf_eval(master, xi)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PprfCopath, AdjacentAndSeparatedSets)
|
||||
{
|
||||
using input_t = std::uint8_t;
|
||||
constexpr std::size_t n = dpf::utils::bitlength_of_v<input_t>;
|
||||
auto master = dpf::make_pprf_master<input_t>();
|
||||
|
||||
const input_t adjacent[] = {2, 3};
|
||||
auto adj = dpf::puncture(master, std::begin(adjacent), std::end(adjacent));
|
||||
// Share a 7-bit prefix: one sibling per shared level, none at the leaves.
|
||||
EXPECT_EQ(adj.nodes.size(), n - 1);
|
||||
EXPECT_LE(adj.nodes.size(), n * adj.hidden.size());
|
||||
EXPECT_LT(adj.nodes.size(), 2 * n);
|
||||
|
||||
const input_t separated[] = {2, 5};
|
||||
auto sep = dpf::puncture(master, std::begin(separated), std::end(separated));
|
||||
EXPECT_LE(sep.nodes.size(), n * sep.hidden.size());
|
||||
EXPECT_GT(sep.nodes.size(), adj.nodes.size());
|
||||
|
||||
for (unsigned x = 0; x < 256; ++x)
|
||||
{
|
||||
const auto xi = static_cast<input_t>(x);
|
||||
const bool in_adj = (xi == 2 || xi == 3);
|
||||
const bool in_sep = (xi == 2 || xi == 5);
|
||||
if (in_adj)
|
||||
EXPECT_THROW((void)dpf::pprf_eval(adj, xi), std::invalid_argument);
|
||||
else
|
||||
EXPECT_TRUE(
|
||||
eq_block(dpf::pprf_eval(adj, xi), dpf::pprf_eval(master, xi)));
|
||||
if (in_sep)
|
||||
EXPECT_THROW((void)dpf::pprf_eval(sep, xi), std::invalid_argument);
|
||||
else
|
||||
EXPECT_TRUE(
|
||||
eq_block(dpf::pprf_eval(sep, xi), dpf::pprf_eval(master, xi)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PprfCopath, HiddenLeafNotPublishedWhenSiblingHidden)
|
||||
{
|
||||
using input_t = std::uint8_t;
|
||||
auto master = dpf::make_pprf_master<input_t>();
|
||||
const input_t leaf = 2;
|
||||
const auto leaf_seed = dpf::pprf_eval(master, leaf);
|
||||
|
||||
const input_t alone[] = {leaf};
|
||||
auto solo = dpf::puncture(master, std::begin(alone), std::end(alone),
|
||||
/*program_hidden=*/true);
|
||||
EXPECT_TRUE(node_has_seed(solo, dpf::pprf_eval(master, static_cast<input_t>(3))));
|
||||
|
||||
const input_t both[] = {2, 3};
|
||||
auto pair = dpf::puncture(master, std::begin(both), std::end(both),
|
||||
/*program_hidden=*/true);
|
||||
EXPECT_FALSE(node_has_seed(pair, leaf_seed));
|
||||
EXPECT_FALSE(node_has_seed(pair, dpf::pprf_eval(master, static_cast<input_t>(3))));
|
||||
ASSERT_EQ(pair.programmed.size(), 2u);
|
||||
EXPECT_TRUE(eq_block(pair.programmed[0], leaf_seed));
|
||||
EXPECT_TRUE(eq_block(pair.programmed[1],
|
||||
dpf::pprf_eval(master, static_cast<input_t>(3))));
|
||||
EXPECT_TRUE(eq_block(dpf::pprf_eval(pair, leaf), leaf_seed));
|
||||
EXPECT_TRUE(eq_block(dpf::pprf_eval(pair, static_cast<input_t>(3)),
|
||||
dpf::pprf_eval(master, static_cast<input_t>(3))));
|
||||
}
|
||||
|
||||
TEST(PprfCopath, EmptyAndDuplicates)
|
||||
{
|
||||
using input_t = std::uint8_t;
|
||||
auto master = dpf::make_pprf_master<input_t>();
|
||||
|
||||
const std::vector<input_t> none{};
|
||||
auto empty = dpf::puncture(master, none.begin(), none.end());
|
||||
ASSERT_EQ(empty.nodes.size(), 1u);
|
||||
EXPECT_EQ(empty.nodes[0].level, 0u);
|
||||
EXPECT_EQ(empty.nodes[0].prefix, input_t{});
|
||||
EXPECT_TRUE(eq_block(empty.nodes[0].seed, master.root));
|
||||
EXPECT_TRUE(empty.hidden.empty());
|
||||
for (unsigned x = 0; x < 256; ++x)
|
||||
{
|
||||
const auto xi = static_cast<input_t>(x);
|
||||
EXPECT_TRUE(
|
||||
eq_block(dpf::pprf_eval(empty, xi), dpf::pprf_eval(master, xi)));
|
||||
}
|
||||
|
||||
const input_t dups[] = {5, 2, 5, 2, 5};
|
||||
auto once = dpf::puncture(master, std::begin(dups), std::end(dups));
|
||||
const input_t unique[] = {2, 5};
|
||||
auto clean = dpf::puncture(master, std::begin(unique), std::end(unique));
|
||||
ASSERT_EQ(once.hidden.size(), 2u);
|
||||
EXPECT_EQ(once.hidden, clean.hidden);
|
||||
ASSERT_EQ(once.nodes.size(), clean.nodes.size());
|
||||
for (std::size_t i = 0; i < once.nodes.size(); ++i)
|
||||
{
|
||||
EXPECT_EQ(once.nodes[i].level, clean.nodes[i].level);
|
||||
EXPECT_EQ(once.nodes[i].prefix, clean.nodes[i].prefix);
|
||||
EXPECT_TRUE(eq_block(once.nodes[i].seed, clean.nodes[i].seed));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PprfCopath, FullDomainPublishesNothing)
|
||||
{
|
||||
using input_t = std::uint8_t;
|
||||
auto master = dpf::make_pprf_master<input_t>();
|
||||
std::vector<input_t> all(256);
|
||||
for (unsigned x = 0; x < 256; ++x)
|
||||
all[x] = static_cast<input_t>(x);
|
||||
|
||||
auto bare = dpf::puncture(master, all.begin(), all.end(),
|
||||
/*program_hidden=*/false);
|
||||
EXPECT_TRUE(bare.nodes.empty());
|
||||
ASSERT_EQ(bare.hidden.size(), 256u);
|
||||
EXPECT_TRUE(bare.programmed.empty());
|
||||
for (unsigned x = 0; x < 256; ++x)
|
||||
EXPECT_THROW((void)dpf::pprf_eval(bare, static_cast<input_t>(x)),
|
||||
std::invalid_argument);
|
||||
|
||||
auto programmed = dpf::puncture(master, all.begin(), all.end(),
|
||||
/*program_hidden=*/true);
|
||||
EXPECT_TRUE(programmed.nodes.empty());
|
||||
ASSERT_EQ(programmed.programmed.size(), 256u);
|
||||
for (unsigned x = 0; x < 256; ++x)
|
||||
{
|
||||
const auto xi = static_cast<input_t>(x);
|
||||
EXPECT_TRUE(eq_block(dpf::pprf_eval(programmed, xi),
|
||||
dpf::pprf_eval(master, xi)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PprfCopath, NodePrefixesAreConsistent)
|
||||
{
|
||||
using input_t = std::uint8_t;
|
||||
auto master = dpf::make_pprf_master<input_t>();
|
||||
const input_t hidden[] = {2, 5, 200};
|
||||
auto copath = dpf::puncture(master, std::begin(hidden), std::end(hidden));
|
||||
|
||||
EXPECT_LE(copath.nodes.size(),
|
||||
dpf::utils::bitlength_of_v<input_t> * copath.hidden.size());
|
||||
|
||||
for (const auto & node : copath.nodes)
|
||||
{
|
||||
ASSERT_GE(node.level, 1u);
|
||||
ASSERT_LE(node.level, dpf::utils::bitlength_of_v<input_t>);
|
||||
// Right-aligned prefix fits in `level` bits.
|
||||
if (node.level < dpf::utils::bitlength_of_v<input_t>)
|
||||
{
|
||||
EXPECT_LT(static_cast<unsigned>(node.prefix),
|
||||
1u << node.level);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned x = 0; x < 256; ++x)
|
||||
{
|
||||
const auto xi = static_cast<input_t>(x);
|
||||
if (std::binary_search(std::begin(hidden), std::end(hidden), xi))
|
||||
continue;
|
||||
EXPECT_TRUE(
|
||||
eq_block(dpf::pprf_eval(copath, xi), dpf::pprf_eval(master, xi)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PprfCopath, Uint32SetMatchesMaster)
|
||||
{
|
||||
using input_t = std::uint32_t;
|
||||
auto master = dpf::make_pprf_master<input_t>();
|
||||
const input_t hidden[] = {0u, 0x00c0ffeeu, 0xffffffffu};
|
||||
auto copath = dpf::puncture(master, std::begin(hidden), std::end(hidden),
|
||||
/*program_hidden=*/true);
|
||||
|
||||
ASSERT_EQ(copath.programmed.size(), 3u);
|
||||
for (input_t h : hidden)
|
||||
EXPECT_TRUE(eq_block(dpf::pprf_eval(copath, h), dpf::pprf_eval(master, h)));
|
||||
|
||||
const input_t samples[] = {1u, 2u, 0x00c0ffefu, 0x80000000u, 0xfffffffeu};
|
||||
for (input_t x : samples)
|
||||
{
|
||||
if (std::find(std::begin(hidden), std::end(hidden), x) != std::end(hidden))
|
||||
continue;
|
||||
EXPECT_TRUE(
|
||||
eq_block(dpf::pprf_eval(copath, x), dpf::pprf_eval(master, x)));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue