libdpf/test/tests/wildcard_test.cpp

810 lines
32 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include <tuple>
#include <cstring>
#include <cstdint>
#include "asio.hpp"
#define LIBDPF_HAS_ASIO
#include "dpf.hpp"
template <std::size_t I = 0,
typename DpfKey0,
typename DpfKey1,
typename ConcreteT>
void assign_leaf(DpfKey0 & dpf0, DpfKey1 & dpf1, const ConcreteT y_shr0, const ConcreteT y_shr1)
{
asio::io_context io_context;
std::thread server([&io_context, &dpf0, &y_shr0]()
{
asio::ip::tcp::acceptor acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), 31337));
asio::ip::tcp::socket peer{io_context};
asio::ip::tcp::endpoint ep{};
acceptor.accept(peer, ep);
auto future = dpf0.template async_assign_leaf<I>(peer, y_shr0, asio::use_future);
io_context.run();
future.wait();
});
std::thread client([&io_context, &dpf1, &y_shr1]()
{
asio::ip::tcp::socket peer(io_context);
asio::ip::tcp::resolver resolver(io_context);
asio::connect(peer, resolver.resolve("localhost", "31337"));
auto future = dpf1.template async_assign_leaf<I>(peer, y_shr1, asio::use_future);
io_context.run();
future.wait();
});
client.join();
server.join();
}
TEST(WildcardTest, SingleLeafFailEvalBeforeVernalization)
{
using input_type = uint8_t;
using output_type = dpf::wildcard_value<uint32_t>;
using dpf_type = dpf::utils::dpf_type_t<dpf::prg::aes128, dpf::prg::aes128, input_type, output_type>;
input_type x = 0xAA;
output_type y;
auto [dpf0, dpf1] = dpf::make_dpf(x, y);
ASSERT_THROW(dpf::eval_point(dpf0, x), std::runtime_error);
ASSERT_THROW(dpf::eval_point(dpf1, x), std::runtime_error);
input_type from = 0x33, to = 0xCC;
ASSERT_THROW(dpf::eval_interval(dpf0, from, to), std::runtime_error);
ASSERT_THROW(dpf::eval_interval(dpf1, from, to), std::runtime_error);
ASSERT_THROW(dpf::eval_full(dpf0), std::runtime_error);
ASSERT_THROW(dpf::eval_full(dpf1), std::runtime_error);
std::array<input_type, 16> points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
ASSERT_THROW(dpf::eval_sequence(dpf0, std::begin(points), std::end(points)), std::runtime_error);
ASSERT_THROW(dpf::eval_sequence(dpf1, std::begin(points), std::end(points)), std::runtime_error);
ASSERT_THROW(dpf::eval_sequence_breadth_first(dpf0, std::begin(points), std::end(points)), std::runtime_error);
ASSERT_THROW(dpf::eval_sequence_breadth_first(dpf1, std::begin(points), std::end(points)), std::runtime_error);
auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points));
auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points));
ASSERT_THROW(dpf::eval_sequence(dpf0, recipe0), std::runtime_error);
ASSERT_THROW(dpf::eval_sequence(dpf1, recipe1), std::runtime_error);
}
TEST(WildcardTest, MultiLeafFailEvalBeforeVernalization)
{
using input_type = uint8_t;
using output_type0 = uint32_t;
using output_type1 = dpf::wildcard_value<uint32_t>;
using dpf_type = dpf::utils::dpf_type_t<dpf::prg::aes128, dpf::prg::aes128, input_type, output_type0, output_type1>;
input_type x = 0xAA;
output_type0 y0 = 0xAAAAAAAA;
output_type1 y1;
auto [dpf0, dpf1] = dpf::make_dpf(x, y0, y1);
ASSERT_NO_THROW((dpf::eval_point<0>(dpf0, x)));
ASSERT_NO_THROW((dpf::eval_point<0>(dpf1, x)));
ASSERT_THROW((dpf::eval_point<1>(dpf0, x)), std::runtime_error);
ASSERT_THROW((dpf::eval_point<1>(dpf1, x)), std::runtime_error);
ASSERT_THROW((dpf::eval_point<0, 1>(dpf0, x)), std::runtime_error);
ASSERT_THROW((dpf::eval_point<0, 1>(dpf1, x)), std::runtime_error);
input_type from = 0x33, to = 0xCC;
ASSERT_NO_THROW((dpf::eval_interval<0>(dpf0, from, to)));
ASSERT_NO_THROW((dpf::eval_interval<0>(dpf1, from, to)));
ASSERT_THROW((dpf::eval_interval<1>(dpf0, from, to)), std::runtime_error);
ASSERT_THROW((dpf::eval_interval<1>(dpf1, from, to)), std::runtime_error);
ASSERT_THROW((dpf::eval_interval<0, 1>(dpf0, from, to)), std::runtime_error);
ASSERT_THROW((dpf::eval_interval<0, 1>(dpf1, from, to)), std::runtime_error);
ASSERT_NO_THROW((dpf::eval_full<0>(dpf0)));
ASSERT_NO_THROW((dpf::eval_full<0>(dpf1)));
ASSERT_THROW((dpf::eval_full<1>(dpf0)), std::runtime_error);
ASSERT_THROW((dpf::eval_full<1>(dpf1)), std::runtime_error);
ASSERT_THROW((dpf::eval_full<0, 1>(dpf0)), std::runtime_error);
ASSERT_THROW((dpf::eval_full<0, 1>(dpf1)), std::runtime_error);
std::array<input_type, 16> points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf0, std::begin(points), std::end(points))));
ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf1, std::begin(points), std::end(points))));
ASSERT_THROW((dpf::eval_sequence<1>(dpf0, std::begin(points), std::end(points))), std::runtime_error);
ASSERT_THROW((dpf::eval_sequence<1>(dpf1, std::begin(points), std::end(points))), std::runtime_error);
ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf0, std::begin(points), std::end(points))), std::runtime_error);
ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf1, std::begin(points), std::end(points))), std::runtime_error);
auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points));
auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points));
ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf0, recipe0)));
ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf1, recipe1)));
ASSERT_THROW((dpf::eval_sequence<1>(dpf0, recipe0)), std::runtime_error);
ASSERT_THROW((dpf::eval_sequence<1>(dpf1, recipe1)), std::runtime_error);
ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf0, recipe0)), std::runtime_error);
ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf1, recipe1)), std::runtime_error);
}
TEST(WildcardTest, SingleLeafSuccess)
{
using input_type = uint8_t;
using concrete_type = uint32_t;
using output_type = dpf::wildcard_value<concrete_type>;
using dpf_type = dpf::utils::dpf_type_t<dpf::prg::aes128, dpf::prg::aes128, input_type, output_type>;
input_type x = 0xAA, from = 0x33, to = 0xCC;
output_type y;
concrete_type y_exp = 0xAAAAAAAA,
y_shr0 = 0x12345678,
y_shr1 = y_exp - y_shr0,
zero_output = 0;
auto [dpf0, dpf1] = dpf::make_dpf(x, y);
assign_leaf(dpf0, dpf1, y_shr0, y_shr1);
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i)
{
auto y0 = *dpf::eval_point(dpf0, i);
auto y1 = *dpf::eval_point(dpf1, i);
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(y0, y1), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(y0, y1), zero_output);
}
}
auto [bufint0, iterint0] = dpf::eval_interval(dpf0, from, to);
auto [bufint1, iterint1] = dpf::eval_interval(dpf1, from, to);
auto itint0 = std::begin(iterint0);
auto itint1 = std::begin(iterint1);
for (std::size_t i = from; i <= to; ++i, ++itint0, ++itint1)
{
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(*itint0, *itint1), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itint0, *itint1), zero_output);
}
}
ASSERT_EQ(itint0, std::end(iterint0));
ASSERT_EQ(itint1, std::end(iterint1));
auto [bufful0, iterful0] = dpf::eval_full(dpf0);
auto [bufful1, iterful1] = dpf::eval_full(dpf1);
auto itful0 = std::begin(iterful0);
auto itful1 = std::begin(iterful1);
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i, ++itful0, ++itful1)
{
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(*itful0, *itful1), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itful0, *itful1), zero_output);
}
}
ASSERT_EQ(itful0, std::end(iterful0));
ASSERT_EQ(itful1, std::end(iterful1));
std::array<input_type, 16> points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
auto [bufseq0, iterseq0] = dpf::eval_sequence(dpf0, std::begin(points), std::end(points));
auto [bufseq1, iterseq1] = dpf::eval_sequence(dpf1, std::begin(points), std::end(points));
auto itseq0 = std::begin(iterseq0);
auto itseq1 = std::begin(iterseq1);
for (std::size_t i = 0; i < points.size(); ++i, ++itseq0, ++itseq1)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(*itseq0, *itseq1), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itseq0, *itseq1), zero_output);
}
}
ASSERT_EQ(itseq0, std::end(iterseq0));
ASSERT_EQ(itseq1, std::end(iterseq1));
auto [bufbre0, iterbre0] = dpf::eval_sequence_breadth_first(dpf0, std::begin(points), std::end(points));
auto [bufbre1, iterbre1] = dpf::eval_sequence_breadth_first(dpf1, std::begin(points), std::end(points));
auto itbre0 = std::begin(iterbre0);
auto itbre1 = std::begin(iterbre1);
for (std::size_t i = 0; i < points.size(); ++i, ++itbre0, ++itbre1)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(*itbre0, *itbre1), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itbre0, *itbre1), zero_output);
}
}
ASSERT_EQ(itbre0, std::end(iterbre0));
ASSERT_EQ(itbre1, std::end(iterbre1));
auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points));
auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points));
auto [bufrec0, iterrec0] = dpf::eval_sequence(dpf0, recipe0);
auto [bufrec1, iterrec1] = dpf::eval_sequence(dpf1, recipe1);
auto itrec0 = std::begin(iterrec0);
auto itrec1 = std::begin(iterrec1);
for (std::size_t i = 0; i < points.size(); ++i, ++itrec0, ++itrec1)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(*itrec0, *itrec1), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itrec0, *itrec1), zero_output);
}
}
ASSERT_EQ(itrec0, std::end(iterrec0));
ASSERT_EQ(itrec1, std::end(iterrec1));
}
TEST(WildcardTest, MultiLeafSuccess)
{
using input_type = uint8_t;
using output_type0 = uint32_t;
using concrete_type = uint32_t;
using output_type1 = dpf::wildcard_value<concrete_type>;
using dpf_type = dpf::utils::dpf_type_t<dpf::prg::aes128, dpf::prg::aes128, input_type, output_type0, output_type1>;
input_type x = 0xAA, from = 0x33, to = 0xCC;
output_type0 y0 = 0x55555555,
zero_output0 = 0;
output_type1 y1;
concrete_type y_exp = 0xAAAAAAAA,
y_shr0 = 0x12345678,
y_shr1 = y_exp - y_shr0,
zero_output1 = 0;
auto [dpf0, dpf1] = dpf::make_dpf(x, y0, y1);
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i)
{
auto ypoi0 = *dpf::eval_point<0>(dpf0, i);
auto ypoi1 = *dpf::eval_point<0>(dpf1, i);
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), y0);
}
else
{
ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), zero_output0);
}
}
auto [bufint00, iterint00] = dpf::eval_interval<0>(dpf0, from, to);
auto [bufint01, iterint01] = dpf::eval_interval<0>(dpf1, from, to);
auto itint00 = std::begin(iterint00);
auto itint01 = std::begin(iterint01);
for (std::size_t i = from; i <= to; ++i, ++itint00, ++itint01)
{
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(*itint00, *itint01), y0);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itint00, *itint01), zero_output0);
}
}
ASSERT_EQ(itint00, std::end(iterint00));
ASSERT_EQ(itint01, std::end(iterint01));
auto [bufful00, iterful00] = dpf::eval_full<0>(dpf0);
auto [bufful01, iterful01] = dpf::eval_full<0>(dpf1);
auto itful00 = std::begin(iterful00);
auto itful01 = std::begin(iterful01);
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i, ++itful00, ++itful01)
{
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(*itful00, *itful01), y0);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itful00, *itful01), zero_output0);
}
}
ASSERT_EQ(itful00, std::end(iterful00));
ASSERT_EQ(itful01, std::end(iterful01));
std::array<input_type, 16> points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
auto [bufseq00, iterseq00] = dpf::eval_sequence<0>(dpf0, std::begin(points), std::end(points));
auto [bufseq01, iterseq01] = dpf::eval_sequence<0>(dpf1, std::begin(points), std::end(points));
auto itseq00 = std::begin(iterseq00);
auto itseq01 = std::begin(iterseq01);
for (std::size_t i = 0; i < points.size(); ++i, ++itseq00, ++itseq01)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(*itseq00, *itseq01), y0);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itseq00, *itseq01), zero_output0);
}
}
ASSERT_EQ(itseq00, std::end(iterseq00));
ASSERT_EQ(itseq01, std::end(iterseq01));
auto [bufbre00, iterbre00] = dpf::eval_sequence_breadth_first<0>(dpf0, std::begin(points), std::end(points));
auto [bufbre01, iterbre01] = dpf::eval_sequence_breadth_first<0>(dpf1, std::begin(points), std::end(points));
auto itbre00 = std::begin(iterbre00);
auto itbre01 = std::begin(iterbre01);
for (std::size_t i = 0; i < points.size(); ++i, ++itbre00, ++itbre01)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(*itbre00, *itbre01), y0);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itbre00, *itbre01), zero_output0);
}
}
ASSERT_EQ(itbre00, std::end(iterbre00));
ASSERT_EQ(itbre01, std::end(iterbre01));
auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points));
auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points));
auto [bufrec00, iterrec00] = dpf::eval_sequence<0>(dpf0, recipe0);
auto [bufrec01, iterrec01] = dpf::eval_sequence<0>(dpf1, recipe1);
auto itrec00 = std::begin(iterrec00);
auto itrec01 = std::begin(iterrec01);
for (std::size_t i = 0; i < points.size(); ++i, ++itrec00, ++itrec01)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(*itrec00, *itrec01), y0);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itrec00, *itrec01), zero_output0);
}
}
ASSERT_EQ(itrec00, std::end(iterrec00));
ASSERT_EQ(itrec01, std::end(iterrec01));
assign_leaf<1>(dpf0, dpf1, y_shr0, y_shr1);
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i)
{
auto ypoi0 = *dpf::eval_point<1>(dpf0, i);
auto ypoi1 = *dpf::eval_point<1>(dpf1, i);
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), zero_output1);
}
}
auto [bufint10, iterint10] = dpf::eval_interval<1>(dpf0, from, to);
auto [bufint11, iterint11] = dpf::eval_interval<1>(dpf1, from, to);
auto itint10 = std::begin(iterint10);
auto itint11 = std::begin(iterint11);
for (std::size_t i = from; i <= to; ++i, ++itint10, ++itint11)
{
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(*itint10, *itint11), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itint10, *itint11), zero_output1);
}
}
ASSERT_EQ(itint10, std::end(iterint10));
ASSERT_EQ(itint11, std::end(iterint11));
auto [bufful10, iterful10] = dpf::eval_full<1>(dpf0);
auto [bufful11, iterful11] = dpf::eval_full<1>(dpf1);
auto itful10 = std::begin(iterful10);
auto itful11 = std::begin(iterful11);
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i, ++itful10, ++itful11)
{
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(*itful10, *itful11), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itful10, *itful11), zero_output1);
}
}
ASSERT_EQ(itful10, std::end(iterful10));
ASSERT_EQ(itful11, std::end(iterful11));
auto [bufseq10, iterseq10] = dpf::eval_sequence<1>(dpf0, std::begin(points), std::end(points));
auto [bufseq11, iterseq11] = dpf::eval_sequence<1>(dpf1, std::begin(points), std::end(points));
auto itseq10 = std::begin(iterseq10);
auto itseq11 = std::begin(iterseq11);
for (std::size_t i = 0; i < points.size(); ++i, ++itseq10, ++itseq11)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(*itseq10, *itseq11), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itseq10, *itseq11), zero_output1);
}
}
ASSERT_EQ(itseq10, std::end(iterseq10));
ASSERT_EQ(itseq11, std::end(iterseq11));
auto [bufbre10, iterbre10] = dpf::eval_sequence_breadth_first<1>(dpf0, std::begin(points), std::end(points));
auto [bufbre11, iterbre11] = dpf::eval_sequence_breadth_first<1>(dpf1, std::begin(points), std::end(points));
auto itbre10 = std::begin(iterbre10);
auto itbre11 = std::begin(iterbre11);
for (std::size_t i = 0; i < points.size(); ++i, ++itbre10, ++itbre11)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(*itbre10, *itbre11), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itbre10, *itbre11), zero_output1);
}
}
ASSERT_EQ(itbre10, std::end(iterbre10));
ASSERT_EQ(itbre11, std::end(iterbre11));
auto [bufrec10, iterrec10] = dpf::eval_sequence<1>(dpf0, recipe0);
auto [bufrec11, iterrec11] = dpf::eval_sequence<1>(dpf1, recipe1);
auto itrec10 = std::begin(iterrec10);
auto itrec11 = std::begin(iterrec11);
for (std::size_t i = 0; i < points.size(); ++i, ++itrec10, ++itrec11)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(*itrec10, *itrec11), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(*itrec10, *itrec11), zero_output1);
}
}
ASSERT_EQ(itrec10, std::end(iterrec10));
ASSERT_EQ(itrec11, std::end(iterrec11));
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i)
{
auto ypoi0 = dpf::eval_point<0, 1>(dpf0, i);
auto ypoi1 = dpf::eval_point<0, 1>(dpf1, i);
if (i == x)
{
ASSERT_EQ(static_cast<output_type0>(dpf::reconstruct(std::get<0>(ypoi0), std::get<0>(ypoi1))), y0);
ASSERT_EQ(static_cast<concrete_type>(dpf::reconstruct(std::get<1>(ypoi0), std::get<1>(ypoi1))), y_exp);
}
else
{
ASSERT_EQ(static_cast<output_type0>(dpf::reconstruct(std::get<0>(ypoi0), std::get<0>(ypoi1))), zero_output0);
ASSERT_EQ(static_cast<concrete_type>(dpf::reconstruct(std::get<1>(ypoi0), std::get<1>(ypoi1))), zero_output1);
}
}
auto [bufintmul0, iterintmul0] = dpf::eval_interval<0, 1>(dpf0, from, to);
auto [bufintmul1, iterintmul1] = dpf::eval_interval<0, 1>(dpf1, from, to);
auto zipint0 = dpf::tuple_as_zip(iterintmul0);
auto zipint1 = dpf::tuple_as_zip(iterintmul1);
auto itintmul0 = std::begin(zipint0);
auto itintmul1 = std::begin(zipint1);
for (std::size_t i = from; i <= to; ++i, ++itintmul0, ++itintmul1)
{
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itintmul0), std::get<0>(*itintmul1)), y0);
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itintmul0), std::get<1>(*itintmul1)), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itintmul0), std::get<0>(*itintmul1)), zero_output0);
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itintmul0), std::get<1>(*itintmul1)), zero_output1);
}
}
ASSERT_EQ(itintmul0, std::end(zipint0));
ASSERT_EQ(itintmul1, std::end(zipint1));
auto [buffulmul0, iterfulmul0] = dpf::eval_full<0, 1>(dpf0);
auto [buffulmul1, iterfulmul1] = dpf::eval_full<0, 1>(dpf1);
auto zipful0 = dpf::tuple_as_zip(iterfulmul0);
auto zipful1 = dpf::tuple_as_zip(iterfulmul1);
auto itfulmul0 = std::begin(zipful0);
auto itfulmul1 = std::begin(zipful1);
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i, ++itfulmul0, ++itfulmul1)
{
if (i == x)
{
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itfulmul0), std::get<0>(*itfulmul1)), y0);
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itfulmul0), std::get<1>(*itfulmul1)), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itfulmul0), std::get<0>(*itfulmul1)), zero_output0);
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itfulmul0), std::get<1>(*itfulmul1)), zero_output1);
}
}
ASSERT_EQ(itfulmul0, std::end(zipful0));
ASSERT_EQ(itfulmul1, std::end(zipful1));
auto [bufseqmul0, iterseqmul0] = dpf::eval_sequence<0, 1>(dpf0, std::begin(points), std::end(points));
auto [bufseqmul1, iterseqmul1] = dpf::eval_sequence<0, 1>(dpf1, std::begin(points), std::end(points));
auto zipseq0 = dpf::tuple_as_zip(iterseqmul0);
auto zipseq1 = dpf::tuple_as_zip(iterseqmul1);
auto itseqmul0 = std::begin(zipseq0);
auto itseqmul1 = std::begin(zipseq1);
for (std::size_t i = 0; i < points.size(); ++i, ++itseqmul0, ++itseqmul1)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itseqmul0), std::get<0>(*itseqmul1)), y0);
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itseqmul0), std::get<1>(*itseqmul1)), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itseqmul0), std::get<0>(*itseqmul1)), zero_output0);
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itseqmul0), std::get<1>(*itseqmul1)), zero_output1);
}
}
ASSERT_EQ(itseqmul0, std::end(zipseq0));
ASSERT_EQ(itseqmul1, std::end(zipseq1));
auto [bufrecmul0, iterrecmul0] = dpf::eval_sequence<0, 1>(dpf0, recipe0);
auto [bufrecmul1, iterrecmul1] = dpf::eval_sequence<0, 1>(dpf1, recipe1);
auto ziprec0 = dpf::tuple_as_zip(iterrecmul0);
auto ziprec1 = dpf::tuple_as_zip(iterrecmul1);
auto itrecmul0 = std::begin(ziprec0);
auto itrecmul1 = std::begin(ziprec1);
for (std::size_t i = 0; i < points.size(); ++i, ++itrecmul0, ++itrecmul1)
{
if (points[i] == x)
{
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itrecmul0), std::get<0>(*itrecmul1)), y0);
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itrecmul0), std::get<1>(*itrecmul1)), y_exp);
}
else
{
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itrecmul0), std::get<0>(*itrecmul1)), zero_output0);
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itrecmul0), std::get<1>(*itrecmul1)), zero_output1);
}
}
ASSERT_EQ(itrecmul0, std::end(ziprec0));
ASSERT_EQ(itrecmul1, std::end(ziprec1));
}
TEST(WildcardTest, PackedSmallWildcardsAtNonTerminalAssignAll)
{
// Four packed uint8 wildcards at a non-terminal prefix, plus a concrete
// deepest output. Assign every wildcard leaf through the asio path.
using concrete_t = uint8_t;
dpf::wildcard_value<concrete_t> w;
uint16_t x = 0x55aa;
auto [dpf0, dpf1] = dpf::make_dpf(x,
dpf::at<10>(w, w, w, w),
uint16_t{99});
ASSERT_THROW(dpf::eval_point(dpf::out<0, 10>, dpf0, x), std::runtime_error);
ASSERT_EQ(static_cast<uint16_t>(
dpf::reconstruct(*dpf::eval_point(dpf::out<4>, dpf0, x), *dpf::eval_point(dpf::out<4>, dpf1, x))),
uint16_t{99});
const concrete_t want[4] = {1, 2, 3, 4};
const concrete_t shr0[4] = {10, 20, 30, 40};
assign_leaf<0>(dpf0, dpf1, shr0[0], static_cast<concrete_t>(want[0] - shr0[0]));
assign_leaf<1>(dpf0, dpf1, shr0[1], static_cast<concrete_t>(want[1] - shr0[1]));
assign_leaf<2>(dpf0, dpf1, shr0[2], static_cast<concrete_t>(want[2] - shr0[2]));
assign_leaf<3>(dpf0, dpf1, shr0[3], static_cast<concrete_t>(want[3] - shr0[3]));
ASSERT_EQ(static_cast<concrete_t>(
dpf::reconstruct(*dpf::eval_point(dpf::out<0, 10>, dpf0, x), *dpf::eval_point(dpf::out<0, 10>, dpf1, x))),
want[0]);
ASSERT_EQ(static_cast<concrete_t>(
dpf::reconstruct(*dpf::eval_point(dpf::out<3, 10>, dpf0, x), *dpf::eval_point(dpf::out<3, 10>, dpf1, x))),
want[3]);
ASSERT_EQ(static_cast<concrete_t>(
dpf::reconstruct(
*dpf::eval_point(dpf::out<0, 10>, dpf0,
static_cast<uint16_t>(x ^ (1u << 6))),
*dpf::eval_point(dpf::out<0, 10>, dpf1,
static_cast<uint16_t>(x ^ (1u << 6))))),
concrete_t{0});
}
namespace
{
template <typename Bits, typename T>
T bits_as(Bits bits)
{
T out{};
std::memcpy(&out, &bits, sizeof(T));
return out;
}
template <typename T>
auto bits_of(T v)
{
using bits_t = std::conditional_t<sizeof(T) == 4, std::uint32_t, std::uint64_t>;
bits_t bits{};
std::memcpy(&bits, &v, sizeof(T));
return bits;
}
/// In-process assign for a wildcard leaf (same messages as asio, no socket).
template <std::size_t I = 0, typename DpfKey0, typename DpfKey1, typename ShareT>
void assign_leaf_local(DpfKey0 & dpf0, DpfKey1 & dpf1, const ShareT & shr0,
const ShareT & shr1)
{
auto & w0 = std::get<I>(dpf0.leaf_nodes);
auto & w1 = std::get<I>(dpf1.leaf_nodes);
if (w0.is_ready())
w0.begin_update();
if (w1.is_ready())
w1.begin_update();
const auto b0 = w0.compute_and_get_blinded_output_share(shr0);
const auto b1 = w1.compute_and_get_blinded_output_share(shr1);
const auto l0 = w0.compute_and_get_leaf_share(b1);
const auto l1 = w1.compute_and_get_leaf_share(b0);
w0.reconstruct_correction_word(l1);
w1.reconstruct_correction_word(l0);
}
} // namespace
TEST(WildcardTest, FullWidthXorDoesNotRevealBeta)
{
using input_type = uint8_t;
using concrete_type = dpf::xints::xint128_t;
using output_type = dpf::wildcard_value<concrete_type>;
input_type x = 0x42;
output_type y;
auto [dpf0, dpf1] = dpf::make_dpf(x, y);
auto & w0 = std::get<0>(dpf0.leaf_nodes);
auto & w1 = std::get<0>(dpf1.leaf_nodes);
// Scale Beaver must be planted even for a single full-width XOR lane.
EXPECT_EQ((dpf::outputs_per_leaf_v<concrete_type,
typename std::decay_t<decltype(dpf0)>::exterior_node>), 1u);
EXPECT_TRUE(dpf::utils::has_characteristic_two_v<concrete_type>);
concrete_type y_exp = concrete_type{0x0123456789ABCDEFull};
concrete_type y_shr0 = concrete_type{0x1111111111111111ull};
concrete_type y_shr1 = y_exp + y_shr0; // XOR group
const auto blinded0 = w0.compute_and_get_blinded_output_share(y_shr0);
const auto blinded1 = w1.compute_and_get_blinded_output_share(y_shr1);
// With a non-trivial output blind, the exchanged value is not the share.
EXPECT_NE(blinded0, y_shr0);
EXPECT_NE(blinded1, y_shr1);
// Finish the assign after the privacy check above (state is already blinded).
const auto l0 = w0.compute_and_get_leaf_share(blinded1);
const auto l1 = w1.compute_and_get_leaf_share(blinded0);
w0.reconstruct_correction_word(l1);
w1.reconstruct_correction_word(l0);
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x)),
y_exp);
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, static_cast<input_type>(x ^ 1)),
*dpf::eval_point(dpf1, static_cast<input_type>(x ^ 1))),
concrete_type{});
}
TEST(WildcardTest, FloatWildcardRoundTrip)
{
using input_type = uint8_t;
using concrete_type = float;
using output_type = dpf::wildcard_value<concrete_type>;
input_type x = 0x55;
output_type y;
auto [dpf0, dpf1] = dpf::make_dpf(x, y);
const concrete_type y_exp = 3.14159265f;
const auto y_bits = bits_of(y_exp);
const std::uint32_t shr0_bits = 0xA5A5A5A5u;
const concrete_type y_shr0 = bits_as<std::uint32_t, concrete_type>(shr0_bits);
const concrete_type y_shr1 =
bits_as<std::uint32_t, concrete_type>(y_bits ^ shr0_bits);
assign_leaf_local(dpf0, dpf1, y_shr0, y_shr1);
const auto got = dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x));
EXPECT_EQ(bits_of(got), y_bits);
EXPECT_EQ(bits_of(dpf::reconstruct(
*dpf::eval_point(dpf0, static_cast<input_type>(x ^ 1)),
*dpf::eval_point(dpf1, static_cast<input_type>(x ^ 1)))),
0u);
}
TEST(WildcardTest, SecondAssignUpdatesPayload)
{
using input_type = uint8_t;
using concrete_type = uint32_t;
using output_type = dpf::wildcard_value<concrete_type>;
input_type x = 0x11;
auto [dpf0, dpf1] = dpf::make_dpf(x, output_type{});
const concrete_type beta = 0xAAAAAAAAu;
const concrete_type beta2 = 0xBBBBBBBBu;
const concrete_type s0 = 0x12345678u;
assign_leaf_local(dpf0, dpf1, s0, static_cast<concrete_type>(beta - s0));
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x)), beta);
// Second assign installs (beta2 - beta) on top of the ready leaf.
const concrete_type delta = static_cast<concrete_type>(beta2 - beta);
const concrete_type d0 = 0x01010101u;
assign_leaf_local(dpf0, dpf1, d0, static_cast<concrete_type>(delta - d0));
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x)),
beta2);
}
TEST(WildcardTest, AssignWildcardInputOpensPublicShiftNotAlpha)
{
using input_type = uint8_t;
using output_type = uint32_t;
// Wildcard domain: dealer plants a random mask; parties later open (mask - alpha).
auto [dpf0, dpf1] = dpf::make_dpf(dpf::wildcard_value<input_type>{}, output_type{7});
const input_type mask = static_cast<input_type>(
dpf0.offset_x.raw() + dpf1.offset_x.raw());
const input_type alpha = 0xAAu;
const input_type a0 = 0x12u;
const input_type a1 = static_cast<input_type>(alpha - a0);
const auto sh0 = dpf0.offset_x.compute_and_get_share(a0);
const auto sh1 = dpf1.offset_x.compute_and_get_share(a1);
const auto open0 = dpf0.offset_x.reconstruct(sh1);
const auto open1 = dpf1.offset_x.reconstruct(sh0);
EXPECT_EQ(open0, open1);
const input_type want_shift = static_cast<input_type>(mask - alpha);
EXPECT_EQ(open0, want_shift);
EXPECT_NE(open0, alpha); // public value is the shift, not alpha
EXPECT_EQ(static_cast<input_type>(open0 + alpha), mask);
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, alpha), *dpf::eval_point(dpf1, alpha)),
output_type{7});
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, static_cast<input_type>(alpha ^ 1)),
*dpf::eval_point(dpf1, static_cast<input_type>(alpha ^ 1))),
output_type{0});
}
TEST(WildcardTest, UpdatableTagAssignsThenRewrites)
{
const std::uint8_t alpha = 0x2a;
auto [k0, k1] = dpf::make_dpf(alpha, std::uint64_t{7}, dpf::updatable{});
EXPECT_TRUE(k0.is_wildcard(0));
EXPECT_TRUE(k1.is_wildcard(0));
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(k0, alpha), *dpf::eval_point(k1, alpha)),
std::uint64_t{7});
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(k0, std::uint8_t{0}),
*dpf::eval_point(k1, std::uint8_t{0})),
std::uint64_t{0});
auto & w0 = std::get<0>(k0.leaf_nodes);
auto & w1 = std::get<0>(k1.leaf_nodes);
w0.begin_update();
w1.begin_update();
// A second assign installs the difference β' − β, not the new absolute payload.
const auto shares = dpf::additively_share(std::uint64_t{9} - std::uint64_t{7});
const auto b0 = w0.compute_and_get_blinded_output_share(shares.first.raw());
const auto b1 = w1.compute_and_get_blinded_output_share(shares.second.raw());
const auto l0 = w0.compute_and_get_leaf_share(b1);
const auto l1 = w1.compute_and_get_leaf_share(b0);
w0.reconstruct_correction_word(l1);
w1.reconstruct_correction_word(l0);
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(k0, alpha), *dpf::eval_point(k1, alpha)),
std::uint64_t{9});
}