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>
792 lines
26 KiB
C++
792 lines
26 KiB
C++
/// @file party/flows_coverage.cpp
|
|
/// @brief Party flows that close protocol / type gaps not hit by other amalgams.
|
|
/// @details Amalgamated into run.cpp (do not compile as a second TU). Dealer
|
|
/// (p2) distributes keys for paint / incremental / exotic payloads
|
|
/// because oblivious DS rejects paint kinds. Geneval full/sequence and
|
|
/// arith_output are dealer-run and share-split. CCMP is a 2-party mesh.
|
|
|
|
#include "cases.hpp"
|
|
#include "dist_dpf3.hpp"
|
|
#include "flow_util.hpp"
|
|
#include "key_io.hpp"
|
|
#include "registry.hpp"
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "simde/simde/x86/avx2.h"
|
|
|
|
#include "dpf.hpp"
|
|
#include "dpf/bitstring.hpp"
|
|
#include "dpf/constrained_cmp.hpp"
|
|
#include "dpf/dcf.hpp"
|
|
#include "dpf/dpf3_ds.hpp"
|
|
#include "dpf/eval_full.hpp"
|
|
#include "dpf/eval_inner_product.hpp"
|
|
#include "dpf/eval_point.hpp"
|
|
#include "dpf/eval_sequence.hpp"
|
|
#include "dpf/field128.hpp"
|
|
#include "dpf/geneval.hpp"
|
|
#include "dpf/json.hpp"
|
|
#include "dpf/keyword2.hpp"
|
|
#include "dpf/modint.hpp"
|
|
#include "dpf/p256.hpp"
|
|
#include "grotto/fixedpoint.hpp"
|
|
#include "grotto/nmod.hpp"
|
|
|
|
namespace dpf
|
|
{
|
|
namespace party
|
|
{
|
|
namespace coverage
|
|
{
|
|
|
|
using util::open_additive;
|
|
using util::open_subtractive;
|
|
using util::require;
|
|
using util::role;
|
|
using util::share_bits;
|
|
using util::trio;
|
|
using util::u64;
|
|
|
|
static constexpr char cov_kw_pat[] = "[ab]{8}";
|
|
|
|
// ---- shared helpers --------------------------------------------------------
|
|
|
|
std::uint64_t paint_matched_unit(std::size_t matched, std::uint64_t, bool)
|
|
{
|
|
return static_cast<std::uint64_t>(matched);
|
|
}
|
|
|
|
std::uint64_t lcp_len(std::uint8_t x, std::uint8_t alpha, std::size_t n = 8)
|
|
{
|
|
for (std::size_t i = 0; i < n; ++i)
|
|
{
|
|
const std::uint8_t shift = static_cast<std::uint8_t>(n - 1 - i);
|
|
if (((x >> shift) & 1) != ((alpha >> shift) & 1))
|
|
return static_cast<std::uint64_t>(i);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
template <typename Spec, typename Fn0, typename Fn1>
|
|
void deal_cmp_keys(trio & net, role self, Spec spec, const Fn0 & on0,
|
|
const Fn1 & on1)
|
|
{
|
|
using Input = std::uint8_t;
|
|
const Input alpha = 0xB4;
|
|
if (self == role::p2)
|
|
{
|
|
auto [k0, k1] = make_dpf(alpha, spec);
|
|
send_key(net.to(role::p0), k0);
|
|
send_key(net.to(role::p1), k1);
|
|
return;
|
|
}
|
|
if (self == role::p0)
|
|
{
|
|
using K = std::decay_t<decltype(std::get<0>(make_dpf(Input{}, spec)))>;
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|
on0(key, alpha);
|
|
return;
|
|
}
|
|
using K = std::decay_t<decltype(std::get<1>(make_dpf(Input{}, spec)))>;
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|
on1(key, alpha);
|
|
}
|
|
|
|
template <typename Key>
|
|
void check_paint_grid(trio & net, role self, const Key & key, std::uint8_t alpha,
|
|
auto expect_fn, const char * tag)
|
|
{
|
|
const u64 mask = key.cmp().mask;
|
|
const std::array<std::uint8_t, 8> pts{
|
|
0, 1, alpha, static_cast<std::uint8_t>(alpha ^ 1u), 0x55, 0xAA, 0xFE, 0xFF};
|
|
for (std::uint8_t x : pts)
|
|
{
|
|
const u64 mine = share_bits(eval_point(cmp, key, x)) & mask;
|
|
const u64 opened = open_additive(net, self, mine) & mask;
|
|
if (self == role::p0)
|
|
require(opened == (expect_fn(x, alpha) & mask), tag);
|
|
}
|
|
}
|
|
|
|
// ---- paints (F_DCF path paints; dealer distribute) --------------------------
|
|
|
|
int cov_paint_lcp(role self, trio & net)
|
|
{
|
|
auto on = [&](const auto & key, std::uint8_t alpha) {
|
|
check_paint_grid(net, self, key, alpha,
|
|
[](std::uint8_t x, std::uint8_t a) { return lcp_len(x, a); },
|
|
"paint lcp");
|
|
};
|
|
deal_cmp_keys(net, self, lcp(u64{1}), on, on);
|
|
return 0;
|
|
}
|
|
|
|
int cov_paint_one_hot(role self, trio & net)
|
|
{
|
|
auto on = [&](const auto & key, std::uint8_t alpha) {
|
|
check_paint_grid(net, self, key, alpha,
|
|
[](std::uint8_t x, std::uint8_t a) {
|
|
return 1ULL << lcp_len(x, a);
|
|
},
|
|
"paint one_hot");
|
|
};
|
|
deal_cmp_keys(net, self, diverge_one_hot(u64{1}), on, on);
|
|
return 0;
|
|
}
|
|
|
|
int cov_paint_break_bit(role self, trio & net)
|
|
{
|
|
auto on = [&](const auto & key, std::uint8_t alpha) {
|
|
check_paint_grid(net, self, key, alpha,
|
|
[](std::uint8_t x, std::uint8_t a) {
|
|
const auto d = lcp_len(x, a);
|
|
if (d >= 8)
|
|
return 0ULL;
|
|
return 3ULL * ((a >> (7 - d)) & 1);
|
|
},
|
|
"paint break_bit");
|
|
};
|
|
deal_cmp_keys(net, self, break_bit(u64{3}), on, on);
|
|
return 0;
|
|
}
|
|
|
|
int cov_paint_path(role self, trio & net)
|
|
{
|
|
auto on = [&](const auto & key, std::uint8_t alpha) {
|
|
check_paint_grid(net, self, key, alpha,
|
|
[](std::uint8_t x, std::uint8_t a) { return lcp_len(x, a); },
|
|
"paint path");
|
|
};
|
|
deal_cmp_keys(net, self, path_paint(paint_matched_unit), on, on);
|
|
return 0;
|
|
}
|
|
|
|
int cov_paint_prefix_with_length(role self, trio & net)
|
|
{
|
|
auto on = [&](const auto & key, std::uint8_t alpha) {
|
|
check_paint_grid(net, self, key, alpha,
|
|
[](std::uint8_t x, std::uint8_t a) {
|
|
const auto d = lcp_len(x, a);
|
|
const u64 low = d == 0 ? 0
|
|
: (d >= 8 ? a : static_cast<u64>(a) >> (8 - d));
|
|
return (low << 4) | d;
|
|
},
|
|
"paint prefix_with_length");
|
|
};
|
|
deal_cmp_keys(net, self, prefix_with_length<4>(u64{1}), on, on);
|
|
return 0;
|
|
}
|
|
|
|
// ---- F_IDPF / eq_at / idcf -------------------------------------------------
|
|
|
|
int cov_idpf_at(role self, trio & net)
|
|
{
|
|
using Input = std::uint16_t;
|
|
const Input alpha = 0xa5c3;
|
|
if (self == role::p2)
|
|
{
|
|
auto [k0, k1] = make_dpf(alpha, at<8>(std::uint8_t{42}),
|
|
at<12>(std::uint16_t{7}));
|
|
send_key(net.to(role::p0), k0);
|
|
send_key(net.to(role::p1), k1);
|
|
return 0;
|
|
}
|
|
auto check = [&](const auto & key) {
|
|
const auto y8 = *eval_point(out<0, 8>, key, alpha);
|
|
const auto o8 = open_subtractive(net, self, share_bits(y8));
|
|
if (self == role::p0)
|
|
require(o8 == std::uint8_t{42}, "idpf at8");
|
|
const auto y12 = *eval_point(out<1, 12>, key, alpha);
|
|
const auto o12 = open_subtractive(net, self, share_bits(y12));
|
|
if (self == role::p0)
|
|
require(o12 == std::uint16_t{7}, "idpf at12");
|
|
};
|
|
if (self == role::p0)
|
|
{
|
|
using K = std::decay_t<decltype(std::get<0>(
|
|
make_dpf(Input{}, at<8>(std::uint8_t{}), at<12>(std::uint16_t{}))))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
using K = std::decay_t<decltype(std::get<1>(
|
|
make_dpf(Input{}, at<8>(std::uint8_t{}), at<12>(std::uint16_t{}))))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
|
|
int cov_eq_at(role self, trio & net)
|
|
{
|
|
using Input = std::uint16_t;
|
|
const Input alpha = 0x1234;
|
|
if (self == role::p2)
|
|
{
|
|
auto [k0, k1] = make_dpf(alpha,
|
|
eq_at<8>(std::uint16_t{99}, std::uint16_t{7}));
|
|
send_key(net.to(role::p0), k0);
|
|
send_key(net.to(role::p1), k1);
|
|
return 0;
|
|
}
|
|
auto check = [&](const auto & key) {
|
|
// High 8 bits of alpha match → true branch 99 on that prefix lane.
|
|
const auto y_on = *eval_point(out<0, 8>, key, alpha);
|
|
const auto o_on = open_subtractive(net, self, share_bits(y_on));
|
|
if (self == role::p0)
|
|
require(o_on == std::uint16_t{99}, "eq_at on");
|
|
const Input miss = static_cast<Input>(alpha ^ 0x0100);
|
|
const auto y_off = *eval_point(out<0, 8>, key, miss);
|
|
const auto o_off = open_subtractive(net, self, share_bits(y_off));
|
|
if (self == role::p0)
|
|
require(o_off == std::uint16_t{7}, "eq_at off");
|
|
};
|
|
if (self == role::p0)
|
|
{
|
|
using K = std::decay_t<decltype(std::get<0>(
|
|
make_dpf(Input{}, eq_at<8>(std::uint16_t{}, std::uint16_t{}))))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
using K = std::decay_t<decltype(std::get<1>(
|
|
make_dpf(Input{}, eq_at<8>(std::uint16_t{}, std::uint16_t{}))))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
|
|
int cov_idcf(role self, trio & net)
|
|
{
|
|
using Input = std::uint8_t;
|
|
const Input alpha = 0x40;
|
|
const u64 beta = 5;
|
|
if (self == role::p2)
|
|
{
|
|
auto [k0, k1] = make_dpf(alpha, idcf(gt(beta)));
|
|
send_key(net.to(role::p0), k0);
|
|
send_key(net.to(role::p1), k1);
|
|
return 0;
|
|
}
|
|
auto check = [&](const auto & key) {
|
|
const u64 mask = key.cmp().mask;
|
|
for (Input x : {Input{0x3f}, alpha, Input{0x41}})
|
|
{
|
|
const u64 mine = share_bits(eval_point(cmp, key, x)) & mask;
|
|
const u64 opened = open_additive(net, self, mine) & mask;
|
|
if (self == role::p0)
|
|
{
|
|
const u64 want = x > alpha ? beta : 0u;
|
|
require(opened == want, "idcf gt");
|
|
}
|
|
}
|
|
};
|
|
if (self == role::p0)
|
|
{
|
|
using K = std::decay_t<decltype(std::get<0>(
|
|
make_dpf(Input{}, idcf(gt(u64{})))))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
using K = std::decay_t<decltype(std::get<1>(
|
|
make_dpf(Input{}, idcf(gt(u64{})))))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
|
|
// ---- F_GenEval full / sequence / arith_output ------------------------------
|
|
|
|
struct CovPad
|
|
{
|
|
std::uint64_t n = 1;
|
|
simde__m128i block()
|
|
{
|
|
auto v = simde_mm_set_epi64x(static_cast<long long>(n),
|
|
static_cast<long long>(n * 9 + 3));
|
|
n += 2;
|
|
return v;
|
|
}
|
|
std::uint8_t bit() { return static_cast<std::uint8_t>(n++ & 1u); }
|
|
};
|
|
|
|
simde__m128i cov_roots[32];
|
|
int cov_ri = 0;
|
|
simde__m128i cov_take_root() { return cov_roots[cov_ri++]; }
|
|
|
|
void cov_reset_roots()
|
|
{
|
|
cov_ri = 0;
|
|
for (int i = 0; i < 32; ++i)
|
|
cov_roots[i] = simde_mm_set_epi64x(0x2222 * (i + 1), 0xBEEF0000u + i * 13);
|
|
}
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
|
ds_randomness<simde__m128i (*)(), CovPad> cov_rng()
|
|
{
|
|
return {cov_take_root, CovPad{}};
|
|
}
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
|
|
|
int cov_geneval_full(role self, trio & net)
|
|
{
|
|
using Input = std::uint8_t;
|
|
using Output = std::uint8_t;
|
|
const Input alpha = 0x3c;
|
|
const Input x0 = 0x10;
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|
const Output y = 0x7e;
|
|
if (self == role::p2)
|
|
{
|
|
cov_reset_roots();
|
|
auto g = geneval_full(x0, x1, cov_rng(), y);
|
|
require(g.party0.size() == 256u, "geneval_full size");
|
|
net.to(role::p0).send_vec(g.party0);
|
|
net.to(role::p1).send_vec(g.party1);
|
|
return 0;
|
|
}
|
|
auto mine = net.to(role::p2).recv_vec<Output>();
|
|
require(mine.size() == 256u, "geneval_full recv");
|
|
const Output opened = open_subtractive(net, self, mine[alpha]);
|
|
const Output off = open_subtractive(net, self, mine[0]);
|
|
if (self == role::p0)
|
|
{
|
|
require(opened == y, "geneval_full on");
|
|
require(off == Output{0}, "geneval_full off");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cov_geneval_sequence(role self, trio & net)
|
|
{
|
|
using Input = std::uint8_t;
|
|
using Output = std::uint16_t;
|
|
const Input alpha = 0x2a;
|
|
const Input x0 = 0x07;
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|
const Output y = 99;
|
|
const std::array<Input, 5> seq{0, 1, alpha, 200, 255};
|
|
if (self == role::p2)
|
|
{
|
|
cov_reset_roots();
|
|
auto g = geneval_sequence(x0, x1, seq.begin(), seq.end(), cov_rng(), y);
|
|
require(g.party0.size() == seq.size(), "geneval_seq size");
|
|
net.to(role::p0).send_vec(g.party0);
|
|
net.to(role::p1).send_vec(g.party1);
|
|
return 0;
|
|
}
|
|
auto mine = net.to(role::p2).recv_vec<Output>();
|
|
require(mine.size() == seq.size(), "geneval_seq recv");
|
|
for (std::size_t i = 0; i < seq.size(); ++i)
|
|
{
|
|
const Output opened = open_subtractive(net, self, mine[i]);
|
|
if (self == role::p0)
|
|
require(opened == (seq[i] == alpha ? y : Output{0}), "geneval_seq");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cov_arith_output(role self, trio & net)
|
|
{
|
|
using Input = std::uint8_t;
|
|
using Output = std::uint32_t;
|
|
const Input alpha = 0x2a;
|
|
const Input x0 = 0x55;
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|
const Output beta = 0x01020304;
|
|
const Output y0 = 0x00010002;
|
|
const Output y1 = static_cast<Output>(beta - y0);
|
|
if (self == role::p2)
|
|
{
|
|
cov_reset_roots();
|
|
auto g = geneval_point(arith_output, x0, x1, alpha, cov_rng(), y0, y1);
|
|
require(g.party0.size() == 1u && g.leaf_live, "arith_output live");
|
|
net.to(role::p0).send(net::msg::delta, g.party0[0]);
|
|
net.to(role::p1).send(net::msg::delta, g.party1[0]);
|
|
return 0;
|
|
}
|
|
const Output mine = net.to(role::p2).recv<Output>(net::msg::delta);
|
|
const Output opened = open_subtractive(net, self, mine);
|
|
if (self == role::p0)
|
|
require(opened == beta, "arith_output");
|
|
return 0;
|
|
}
|
|
|
|
// ---- F_CCMP ----------------------------------------------------------------
|
|
|
|
int cov_ccmp(role self, trio & net)
|
|
{
|
|
if (self == role::p2)
|
|
return 0;
|
|
const u64 mine = self == role::p0 ? 4u : 5u;
|
|
const role peer = self == role::p0 ? role::p1 : role::p0;
|
|
std::uint8_t z0 = 0, z1 = 0, l = 0;
|
|
detail::ccmp_party_terms(mine, self == role::p0 ? 0 : 1, z0, z1, l);
|
|
const std::uint8_t peer_z0 = net.exchange_with(peer, z0);
|
|
const std::uint8_t peer_z1 = net.exchange_with(peer, z1);
|
|
const std::uint8_t peer_l = net.exchange_with(peer, l);
|
|
const std::uint8_t opened_z0 = static_cast<std::uint8_t>(z0 ^ peer_z0);
|
|
const std::uint8_t opened_z1 = static_cast<std::uint8_t>(z1 ^ peer_z1);
|
|
const std::uint8_t t = static_cast<std::uint8_t>(opened_z0 & opened_z1);
|
|
const std::uint8_t l1 = self == role::p1 ? l : peer_l;
|
|
const std::uint8_t got = static_cast<std::uint8_t>(t ^ l1);
|
|
require(got == local_ccmp(4u, 5u), "ccmp mesh");
|
|
require(got == 1u, "ccmp 4<5");
|
|
bool bad = false;
|
|
try
|
|
{
|
|
(void)local_ccmp(4u, 6u);
|
|
}
|
|
catch (const std::invalid_argument &)
|
|
{
|
|
bad = true;
|
|
}
|
|
require(bad, "ccmp rejects");
|
|
return 0;
|
|
}
|
|
|
|
// ---- exotic payloads / domains ---------------------------------------------
|
|
|
|
template <typename Output>
|
|
int cov_payload_point(role self, trio & net, Output beta, const char * tag)
|
|
{
|
|
using Input = std::uint8_t;
|
|
const Input alpha = 0x11;
|
|
if (self == role::p2)
|
|
{
|
|
auto [k0, k1] = make_dpf(alpha, beta);
|
|
send_key(net.to(role::p0), k0);
|
|
send_key(net.to(role::p1), k1);
|
|
return 0;
|
|
}
|
|
auto check = [&](const auto & key) {
|
|
const auto on = *eval_point(key, alpha);
|
|
const auto off = *eval_point(key, static_cast<Input>(alpha ^ 1u));
|
|
const Output o_on = open_subtractive(net, self, share_bits(on));
|
|
const Output o_off = open_subtractive(net, self, share_bits(off));
|
|
if (self == role::p0)
|
|
{
|
|
require(o_on == beta, tag);
|
|
require(o_off == Output{}, tag);
|
|
}
|
|
};
|
|
if (self == role::p0)
|
|
{
|
|
using K = std::decay_t<decltype(std::get<0>(make_dpf(Input{}, beta)))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
using K = std::decay_t<decltype(std::get<1>(make_dpf(Input{}, beta)))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
|
|
int cov_field128(role self, trio & net)
|
|
{
|
|
return cov_payload_point(self, net, field128{42}, "field128");
|
|
}
|
|
|
|
int cov_gf2(role self, trio & net)
|
|
{
|
|
return cov_payload_point(self, net, dpf::gf2{1}, "gf2");
|
|
}
|
|
|
|
int cov_gf22(role self, trio & net)
|
|
{
|
|
return cov_payload_point(self, net, dpf::gf22{3}, "gf22");
|
|
}
|
|
|
|
int cov_gf24(role self, trio & net)
|
|
{
|
|
return cov_payload_point(self, net, dpf::gf24{0xa}, "gf24");
|
|
}
|
|
|
|
int cov_gf28(role self, trio & net)
|
|
{
|
|
return cov_payload_point(self, net, dpf::gf28{0x1b}, "gf28");
|
|
}
|
|
|
|
int cov_gf216(role self, trio & net)
|
|
{
|
|
return cov_payload_point(self, net, dpf::gf216{0x2d}, "gf216");
|
|
}
|
|
|
|
int cov_gf232(role self, trio & net)
|
|
{
|
|
return cov_payload_point(self, net, dpf::gf232{0x90200001u}, "gf232");
|
|
}
|
|
|
|
int cov_gf264(role self, trio & net)
|
|
{
|
|
return cov_payload_point(self, net, dpf::gf264{0x11}, "gf264");
|
|
}
|
|
|
|
int cov_p256(role self, trio & net)
|
|
{
|
|
return cov_payload_point(self, net, p256{1}, "p256");
|
|
}
|
|
|
|
int cov_bitstring(role self, trio & net)
|
|
{
|
|
using bs = bitstring<16>;
|
|
return cov_payload_point(self, net, bs{0xBEEFu}, "bitstring");
|
|
}
|
|
|
|
int cov_fixedpoint(role self, trio & net)
|
|
{
|
|
using fp = grotto::fixedpoint<16>;
|
|
return cov_payload_point(self, net, fp::from_raw(0x00018000), "fixedpoint");
|
|
}
|
|
|
|
int cov_keyword2(role self, trio & net)
|
|
{
|
|
using kw = keyword2<cov_kw_pat>;
|
|
const kw alpha{"abababab"};
|
|
const u64 beta = 3;
|
|
if (self == role::p2)
|
|
{
|
|
auto [k0, k1] = make_dpf(alpha, beta);
|
|
send_key(net.to(role::p0), k0);
|
|
send_key(net.to(role::p1), k1);
|
|
return 0;
|
|
}
|
|
auto check = [&](const auto & key) {
|
|
const auto on = *eval_point(key, alpha);
|
|
const auto off = *eval_point(key, kw{"babababa"});
|
|
const auto o_on = open_subtractive(net, self, share_bits(on));
|
|
const auto o_off = open_subtractive(net, self, share_bits(off));
|
|
if (self == role::p0)
|
|
{
|
|
require(o_on == beta, "keyword2 on");
|
|
require(o_off == 0u, "keyword2 off");
|
|
}
|
|
};
|
|
if (self == role::p0)
|
|
{
|
|
using K = std::decay_t<decltype(std::get<0>(make_dpf(kw{}, u64{})))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
using K = std::decay_t<decltype(std::get<1>(make_dpf(kw{}, u64{})))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
|
|
int cov_modint_domain(role self, trio & net)
|
|
{
|
|
using in_t = modint<10>;
|
|
const in_t alpha{3};
|
|
if (self == role::p2)
|
|
{
|
|
auto [k0, k1] = make_dpf(alpha, at<6>(std::uint8_t{1}), std::uint16_t{2});
|
|
send_key(net.to(role::p0), k0);
|
|
send_key(net.to(role::p1), k1);
|
|
return 0;
|
|
}
|
|
auto check = [&](const auto & key) {
|
|
const auto y0 = *eval_point(out<0, 6>, key, alpha);
|
|
const auto o0 = open_subtractive(net, self, share_bits(y0));
|
|
if (self == role::p0)
|
|
require(o0 == std::uint8_t{1}, "modint at6");
|
|
const auto y1 = *eval_point(out<1>, key, alpha);
|
|
const auto o1 = open_subtractive(net, self, share_bits(y1));
|
|
if (self == role::p0)
|
|
require(o1 == std::uint16_t{2}, "modint leaf");
|
|
};
|
|
if (self == role::p0)
|
|
{
|
|
using K = std::decay_t<decltype(std::get<0>(
|
|
make_dpf(in_t{}, at<6>(std::uint8_t{}), std::uint16_t{})))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
using K = std::decay_t<decltype(std::get<1>(
|
|
make_dpf(in_t{}, at<6>(std::uint8_t{}), std::uint16_t{})))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
|
|
// ---- grotto nmod (public reduction helper over the mesh) -------------------
|
|
|
|
int cov_nmod(role self, trio & net)
|
|
{
|
|
// 42 / 10 with 1/10 ≈ 0x199999999999999a / 2^64, keep 8 residue bits.
|
|
const std::int64_t x_raw = 42;
|
|
const unsigned x_bits = 0;
|
|
const unsigned __int128 recip = 0x199999999999999aull;
|
|
const unsigned recip_bits = 64;
|
|
const unsigned residue_bits = 8;
|
|
auto got = grotto::nmod(x_raw, x_bits, recip, recip_bits, residue_bits);
|
|
if (self == role::p2)
|
|
{
|
|
net.to(role::p0).send(net::msg::delta, got.quotient);
|
|
net.to(role::p0).send(net::msg::delta, got.residue);
|
|
net.to(role::p1).send(net::msg::delta, got.quotient);
|
|
net.to(role::p1).send(net::msg::delta, got.residue);
|
|
return 0;
|
|
}
|
|
const auto q = net.to(role::p2).recv<std::int64_t>(net::msg::delta);
|
|
const auto r = net.to(role::p2).recv<std::int64_t>(net::msg::delta);
|
|
require(q == got.quotient && r == got.residue, "nmod agree");
|
|
require(q == 4 && r != 0, "nmod 42/10");
|
|
return 0;
|
|
}
|
|
|
|
// ---- JSON key round-trip over the mesh -------------------------------------
|
|
|
|
int cov_json_roundtrip(role self, trio & net)
|
|
{
|
|
using Input = std::uint8_t;
|
|
const Input alpha = 0x2a;
|
|
const u64 beta = 17;
|
|
if (self == role::p2)
|
|
{
|
|
auto [k0, k1] = make_dpf(alpha, beta);
|
|
const std::string s0 = json::to_json(k0);
|
|
const std::string s1 = json::to_json(k1);
|
|
net.to(role::p0).send_bytes(net::msg::dpf_key,
|
|
reinterpret_cast<const std::uint8_t *>(s0.data()), s0.size());
|
|
net.to(role::p1).send_bytes(net::msg::dpf_key,
|
|
reinterpret_cast<const std::uint8_t *>(s1.data()), s1.size());
|
|
return 0;
|
|
}
|
|
auto body = net.to(role::p2).recv_bytes(net::msg::dpf_key);
|
|
const std::string s(reinterpret_cast<const char *>(body.data()), body.size());
|
|
using K0 = std::decay_t<decltype(std::get<0>(make_dpf(Input{}, u64{})))>;
|
|
using K1 = std::decay_t<decltype(std::get<1>(make_dpf(Input{}, u64{})))>;
|
|
if (self == role::p0)
|
|
{
|
|
auto key = json::from_json<K0>(s);
|
|
const auto y = *eval_point(key, alpha);
|
|
const auto open = open_subtractive(net, self, share_bits(y));
|
|
require(open == beta, "json on");
|
|
return 0;
|
|
}
|
|
auto key = json::from_json<K1>(s);
|
|
const auto y = *eval_point(key, alpha);
|
|
(void)open_subtractive(net, self, share_bits(y));
|
|
return 0;
|
|
}
|
|
|
|
// ---- F_DPF3DS local API (dealer make_dpf3_doerner_shelat) -------------------
|
|
|
|
int cov_dpf3_ds_local(role self, trio & net)
|
|
{
|
|
using Input = std::uint8_t;
|
|
const Input alpha = 0x2a;
|
|
const Input x0 = 0x11;
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|
const fp61 beta{9};
|
|
if (self == role::p2)
|
|
{
|
|
auto [k1, k2, k3] = make_dpf3_doerner_shelat(x0, x1, beta);
|
|
send_key(net.to(role::p0), k1);
|
|
send_key(net.to(role::p1), k2);
|
|
const fp61 y = eval_point(k3, alpha);
|
|
const fp61 got = recent::open_shamir3(net, self, y);
|
|
require(got == beta, "dpf3_ds on");
|
|
const fp61 z = eval_point(k3, static_cast<Input>(alpha ^ 1u));
|
|
const fp61 off = recent::open_shamir3(net, self, z);
|
|
require(off == fp61{}, "dpf3_ds off");
|
|
return 0;
|
|
}
|
|
if (self == role::p0)
|
|
{
|
|
using K = std::decay_t<decltype(std::get<0>(
|
|
make_dpf3_doerner_shelat(Input{}, Input{}, fp61{})))>;
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|
(void)recent::open_shamir3(net, self, eval_point(key, alpha));
|
|
(void)recent::open_shamir3(net, self,
|
|
eval_point(key, static_cast<Input>(alpha ^ 1u)));
|
|
return 0;
|
|
}
|
|
using K = std::decay_t<decltype(std::get<1>(
|
|
make_dpf3_doerner_shelat(Input{}, Input{}, fp61{})))>;
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|
(void)recent::open_shamir3(net, self, eval_point(key, alpha));
|
|
(void)recent::open_shamir3(net, self,
|
|
eval_point(key, static_cast<Input>(alpha ^ 1u)));
|
|
return 0;
|
|
}
|
|
|
|
// ---- eval_inner_product on a point DPF -------------------------------------
|
|
|
|
int cov_eval_inner_product(role self, trio & net)
|
|
{
|
|
using Input = std::uint8_t;
|
|
const Input alpha = 42;
|
|
const u64 beta = 7;
|
|
if (self == role::p2)
|
|
{
|
|
auto [k0, k1] = make_dpf(alpha, beta);
|
|
send_key(net.to(role::p0), k0);
|
|
send_key(net.to(role::p1), k1);
|
|
return 0;
|
|
}
|
|
auto check = [&](const auto & key) {
|
|
std::vector<u64> w(11, 1);
|
|
const u64 mine = share_bits(
|
|
eval_inner_product(paired, key, Input{40}, Input{50}, w));
|
|
// Point leaves are subtractive; the guided-tour reconstruct is additive
|
|
// on the opened difference after both parties contribute.
|
|
const u64 opened = open_subtractive(net, self, mine);
|
|
if (self == role::p0)
|
|
require(opened == beta * w[2], "eval_inner_product");
|
|
};
|
|
if (self == role::p0)
|
|
{
|
|
using K = std::decay_t<decltype(std::get<0>(make_dpf(Input{}, u64{})))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
using K = std::decay_t<decltype(std::get<1>(make_dpf(Input{}, u64{})))>;
|
|
check(recv_key<K>(net.to(role::p2)));
|
|
return 0;
|
|
}
|
|
|
|
#define REG(name, tags, fn) \
|
|
register_flow(flow{#name, tags, fn, false})
|
|
|
|
} // namespace coverage
|
|
|
|
void register_coverage_flows()
|
|
{
|
|
using namespace coverage;
|
|
REG(cov_paint_lcp, "coverage paint dcf", cov_paint_lcp);
|
|
REG(cov_paint_one_hot, "coverage paint dcf", cov_paint_one_hot);
|
|
REG(cov_paint_break_bit, "coverage paint dcf", cov_paint_break_bit);
|
|
REG(cov_paint_path, "coverage paint dcf", cov_paint_path);
|
|
REG(cov_paint_prefix_with_length, "coverage paint dcf",
|
|
cov_paint_prefix_with_length);
|
|
REG(cov_idpf_at, "coverage idpf", cov_idpf_at);
|
|
REG(cov_eq_at, "coverage eq_at", cov_eq_at);
|
|
REG(cov_idcf, "coverage idcf", cov_idcf);
|
|
REG(cov_geneval_full, "coverage geneval", cov_geneval_full);
|
|
REG(cov_geneval_sequence, "coverage geneval", cov_geneval_sequence);
|
|
REG(cov_arith_output, "coverage arith_output", cov_arith_output);
|
|
REG(cov_ccmp, "coverage ccmp", cov_ccmp);
|
|
REG(cov_field128, "coverage payload", cov_field128);
|
|
REG(cov_gf2, "coverage payload", cov_gf2);
|
|
REG(cov_gf22, "coverage payload", cov_gf22);
|
|
REG(cov_gf24, "coverage payload", cov_gf24);
|
|
REG(cov_gf28, "coverage payload", cov_gf28);
|
|
REG(cov_gf216, "coverage payload", cov_gf216);
|
|
REG(cov_gf232, "coverage payload", cov_gf232);
|
|
REG(cov_gf264, "coverage payload", cov_gf264);
|
|
REG(cov_p256, "coverage payload", cov_p256);
|
|
REG(cov_bitstring, "coverage payload", cov_bitstring);
|
|
REG(cov_fixedpoint, "coverage payload", cov_fixedpoint);
|
|
REG(cov_keyword2, "coverage keyword2", cov_keyword2);
|
|
REG(cov_modint_domain, "coverage modint", cov_modint_domain);
|
|
REG(cov_nmod, "coverage nmod", cov_nmod);
|
|
REG(cov_json_roundtrip, "coverage json", cov_json_roundtrip);
|
|
REG(cov_dpf3_ds_local, "coverage dpf3_ds", cov_dpf3_ds_local);
|
|
REG(cov_eval_inner_product, "coverage eval_inner_product",
|
|
cov_eval_inner_product);
|
|
}
|
|
|
|
#undef REG
|
|
|
|
} // namespace party
|
|
} // namespace dpf
|