libdpf/test/tests/leaf_later_test.cpp
Ryan Henry 0d22946a0e 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>
2026-09-28 05:59:19 -06:00

149 lines
4.6 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <gtest/gtest.h>
#include <cstdint>
#include <utility>
#include <vector>
#include "dpf.hpp"
namespace
{
// Full-block payload ⇒ outputs_per_leaf = 1, so a single public F matches the
// leaf correction word share-for-share.
using input_t = std::uint8_t;
using output_t = simde_uint128;
constexpr std::size_t n = 256;
output_t leaf_cw_as_output(const dpf::utils::dpf_type_t<dpf::prg::aes128,
dpf::prg::aes128, input_t, output_t> & key)
{
using exterior = typename std::decay_t<decltype(key)>::exterior_node;
return dpf::extract_leaf<exterior, output_t>(key.template leaf<0>(), 0);
}
// party_key wrappers from make_dpf
template <typename Key>
output_t leaf_cw_as_output(const Key & key)
{
using exterior = typename Key::exterior_node;
return dpf::extract_leaf<exterior, output_t>(key.template leaf<0>(), 0);
}
output_t make_payload(std::uint64_t lo)
{
return output_t{lo};
}
} // namespace
TEST(LeafLater, MatchesCorrectedImmediately)
{
const input_t alpha = 42;
const output_t beta = make_payload(7);
auto [k0, k1] = dpf::make_dpf(alpha, beta);
// Public leaf CW (same on both keys); applying it recovers the immediate path.
const output_t F = leaf_cw_as_output(k0);
EXPECT_EQ(F, leaf_cw_as_output(k1));
auto full0 = dpf::eval_full(k0);
auto full1 = dpf::eval_full(k1);
std::vector<output_t> buf0(n), buf1(n);
std::vector<std::uint8_t> c0(n), c1(n);
dpf::eval_full(buf0, c0, k0, dpf::leaf_later{});
dpf::eval_full(buf1, c1, k1, dpf::leaf_later{});
dpf::apply_leaf_correction(buf0, c0, F);
dpf::apply_leaf_correction(buf1, c1, F);
auto it0 = std::begin(full0.second);
auto it1 = std::begin(full1.second);
for (std::size_t i = 0; i < n; ++i, ++it0, ++it1)
{
const auto imm0 = dpf::detail_walk::group_value(*it0);
const auto imm1 = dpf::detail_walk::group_value(*it1);
EXPECT_EQ(buf0[i], imm0) << "party0 @" << i;
EXPECT_EQ(buf1[i], imm1) << "party1 @" << i;
EXPECT_EQ(buf0[i] - buf1[i], imm0 - imm1) << i;
const auto got = buf0[i] - buf1[i];
if (i == alpha)
EXPECT_EQ(got, beta);
else
EXPECT_EQ(got, output_t{0});
}
}
TEST(LeafLater, ApplyAfterRotateMatchesCorrectThenRotate)
{
const input_t alpha = 10;
const output_t beta = make_payload(99);
const std::size_t s = 32;
auto [k0, k1] = dpf::make_dpf(alpha, beta);
const output_t F = leaf_cw_as_output(k0);
auto full0 = dpf::eval_full(k0);
auto full1 = dpf::eval_full(k1);
std::vector<output_t> early0(n), early1(n);
{
auto a = std::begin(full0.second);
auto b = std::begin(full1.second);
for (std::size_t i = 0; i < n; ++i, ++a, ++b)
{
early0[i] = dpf::detail_walk::group_value(*a);
early1[i] = dpf::detail_walk::group_value(*b);
}
}
early0 = dpf::cyclic_shift(early0, s);
early1 = dpf::cyclic_shift(early1, s);
std::vector<output_t> late0(n), late1(n);
std::vector<std::uint8_t> c0(n), c1(n);
dpf::eval_full(late0, c0, k0, dpf::leaf_later{});
dpf::eval_full(late1, c1, k1, dpf::leaf_later{});
dpf::cyclic_shift_pair(late0, c0, s);
dpf::cyclic_shift_pair(late1, c1, s);
dpf::apply_leaf_correction(late0, c0, F);
dpf::apply_leaf_correction(late1, c1, F);
for (std::size_t i = 0; i < n; ++i)
{
EXPECT_EQ(late0[i], early0[i]) << i;
EXPECT_EQ(late1[i], early1[i]) << i;
}
}
TEST(LeafLater, DuoramShapedUpdate)
{
// Unit at r, rotate by i*−r, then apply a public F built from the leaf CW
// of a key keyed at the online message (same as correcting immediately for
// that message). Neighbours stay unchanged.
const input_t r = 10;
const input_t i_star = 42;
const std::size_t shift = static_cast<std::size_t>(i_star - r);
const output_t message = make_payload(7);
auto [k0, k1] = dpf::make_dpf(r, message);
const output_t F = leaf_cw_as_output(k0);
std::vector<output_t> mem0(n), mem1(n);
mem0[i_star] = make_payload(100);
std::vector<output_t> v0(n), v1(n);
std::vector<std::uint8_t> t0(n), t1(n);
dpf::eval_full_add_into(v0, t0, k0, dpf::leaf_later{}, dpf::rotate{shift});
dpf::eval_full_add_into(v1, t1, k1, dpf::leaf_later{}, dpf::rotate{shift});
dpf::apply_leaf_correction(v0, t0, F);
dpf::apply_leaf_correction(v1, t1, F);
for (std::size_t i = 0; i < n; ++i)
{
mem0[i] = mem0[i] + v0[i];
mem1[i] = mem1[i] + v1[i];
}
EXPECT_EQ(mem0[i_star] - mem1[i_star], make_payload(100) + message);
EXPECT_EQ(mem0[r] - mem1[r], output_t{0});
EXPECT_EQ(mem0[0] - mem1[0], output_t{0});
}