1655 lines
62 KiB
C++
1655 lines
62 KiB
C++
|
|
/// @file party/flows_recent.cpp
|
|||
|
|
/// @brief (2+1) flows for the newest DPF and Grotto surfaces.
|
|||
|
|
/// @details Amalgamated into run.cpp (do not compile as a second TU).
|
|||
|
|
/// Full uint8 domains, and checks that a corrupted proof, seed,
|
|||
|
|
/// correction word, MAC tag, or sketch fails closed.
|
|||
|
|
|
|||
|
|
#include "cases.hpp"
|
|||
|
|
#include "dist_dpf3.hpp"
|
|||
|
|
#include "dist_ds.hpp"
|
|||
|
|
#include "flow_util.hpp"
|
|||
|
|
#include "key_io.hpp"
|
|||
|
|
#include "registry.hpp"
|
|||
|
|
|
|||
|
|
#include <cstdint>
|
|||
|
|
#include <cstring>
|
|||
|
|
#include <stdexcept>
|
|||
|
|
#include <type_traits>
|
|||
|
|
#include <vector>
|
|||
|
|
|
|||
|
|
#include "simde/simde/x86/avx2.h"
|
|||
|
|
|
|||
|
|
#include "dpf/blocked_dcf.hpp"
|
|||
|
|
#include "dpf/dcf.hpp"
|
|||
|
|
#include "dpf/dpf3.hpp"
|
|||
|
|
#include "dpf/dpf3_cmp.hpp"
|
|||
|
|
#include "dpf/dpf3_ds.hpp"
|
|||
|
|
#include "dpf/dpf3_multipoint.hpp"
|
|||
|
|
#include "dpf/eval_full.hpp"
|
|||
|
|
#include "dpf/eval_point.hpp"
|
|||
|
|
#include "dpf/fp61.hpp"
|
|||
|
|
#include "dpf/geneval.hpp"
|
|||
|
|
#include "dpf/interval.hpp"
|
|||
|
|
#include "dpf/multipoint.hpp"
|
|||
|
|
#include "dpf/prg_aes_ccr.hpp"
|
|||
|
|
#include "dpf/shamir3.hpp"
|
|||
|
|
#include "dpf/verifiable.hpp"
|
|||
|
|
#include "grotto/closed_form.hpp"
|
|||
|
|
#include "grotto/exact_steps.hpp"
|
|||
|
|
#include "grotto/offset_poly.hpp"
|
|||
|
|
#include "grotto/offset_jet.hpp"
|
|||
|
|
#include "grotto/offset_repr.hpp"
|
|||
|
|
#include "grotto/offset_twist.hpp"
|
|||
|
|
#include "grotto/ring_switch.hpp"
|
|||
|
|
#include "grotto/residue.hpp"
|
|||
|
|
|
|||
|
|
namespace dpf
|
|||
|
|
{
|
|||
|
|
namespace party
|
|||
|
|
{
|
|||
|
|
namespace recent
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
using util::open_additive;
|
|||
|
|
using util::open_and_sketch;
|
|||
|
|
using util::open_subtractive;
|
|||
|
|
using util::require;
|
|||
|
|
using util::role;
|
|||
|
|
using util::share_bits;
|
|||
|
|
using util::trio;
|
|||
|
|
using u64 = std::uint64_t;
|
|||
|
|
|
|||
|
|
std::vector<u64> exchange_u64(trio & net, role self, const std::vector<u64> & mine)
|
|||
|
|
{
|
|||
|
|
role peer = self == role::p0 ? role::p1 : role::p0;
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
net.to(peer).send_vec(mine);
|
|||
|
|
return net.to(peer).recv_vec<u64>();
|
|||
|
|
}
|
|||
|
|
auto theirs = net.to(peer).recv_vec<u64>();
|
|||
|
|
net.to(peer).send_vec(mine);
|
|||
|
|
return theirs;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::vector<std::uint8_t> exchange_u8(trio & net, role self,
|
|||
|
|
const std::vector<std::uint8_t> & mine)
|
|||
|
|
{
|
|||
|
|
role peer = self == role::p0 ? role::p1 : role::p0;
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
net.to(peer).send_vec(mine);
|
|||
|
|
return net.to(peer).recv_vec<std::uint8_t>();
|
|||
|
|
}
|
|||
|
|
auto theirs = net.to(peer).recv_vec<std::uint8_t>();
|
|||
|
|
net.to(peer).send_vec(mine);
|
|||
|
|
return theirs;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void send_proofs(net::channel & c, const std::vector<proof_token> & v)
|
|||
|
|
{
|
|||
|
|
c.send_bytes(net::msg::proof_token,
|
|||
|
|
reinterpret_cast<const std::uint8_t *>(v.data()),
|
|||
|
|
v.size() * sizeof(proof_token));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::vector<proof_token> recv_proofs(net::channel & c, std::size_t n)
|
|||
|
|
{
|
|||
|
|
auto body = c.recv_bytes(net::msg::proof_token);
|
|||
|
|
require(body.size() == n * sizeof(proof_token), "proof bytes");
|
|||
|
|
std::vector<proof_token> out(n);
|
|||
|
|
std::memcpy(out.data(), body.data(), body.size());
|
|||
|
|
return out;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::vector<proof_token> exchange_proofs(trio & net, role self,
|
|||
|
|
const std::vector<proof_token> & mine)
|
|||
|
|
{
|
|||
|
|
role peer = self == role::p0 ? role::p1 : role::p0;
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
send_proofs(net.to(peer), mine);
|
|||
|
|
return recv_proofs(net.to(peer), mine.size());
|
|||
|
|
}
|
|||
|
|
auto theirs = recv_proofs(net.to(peer), mine.size());
|
|||
|
|
send_proofs(net.to(peer), mine);
|
|||
|
|
return theirs;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
proof_token exchange_proof(trio & net, role self, proof_token mine)
|
|||
|
|
{
|
|||
|
|
std::vector<proof_token> one{mine};
|
|||
|
|
return exchange_proofs(net, self, one)[0];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template <typename Key>
|
|||
|
|
void flip_seeds(Key & key)
|
|||
|
|
{
|
|||
|
|
using arr = typename std::decay_t<Key>::correction_seeds_array;
|
|||
|
|
for (auto & cs : const_cast<arr &>(key.correction_seeds()))
|
|||
|
|
cs[0] = simde_mm_xor_si128(cs[0], simde_mm_set1_epi8(1));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template <typename Key>
|
|||
|
|
void flip_words(Key & key)
|
|||
|
|
{
|
|||
|
|
using arr = typename std::decay_t<Key>::correction_words_array;
|
|||
|
|
for (auto & word : const_cast<arr &>(key.correction_words()))
|
|||
|
|
word = simde_mm_xor_si128(word, simde_mm_set1_epi8(0x5a));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_half_tree_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
using Ht = prg::aes128_ccr;
|
|||
|
|
const Input alpha = 0;
|
|||
|
|
const Input x0 = 0x37;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const u64 beta = 0x1111;
|
|||
|
|
auto on = [&](const auto & key) {
|
|||
|
|
std::vector<u64> shares(256);
|
|||
|
|
std::vector<proof_token> pis(256);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
shares[x] = share_bits(*eval_point(
|
|||
|
|
key, static_cast<Input>(x), prove(pis[x])));
|
|||
|
|
}
|
|||
|
|
auto peer_s = exchange_u64(net, self, shares);
|
|||
|
|
auto peer_p = exchange_proofs(net, self, pis);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
require(shares[x] - peer_s[x]
|
|||
|
|
== (x == alpha ? beta : 0u),
|
|||
|
|
"half-tree domain value");
|
|||
|
|
require(verify(pis[x], peer_p[x]),
|
|||
|
|
"half-tree domain proof");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
require_tree_prefix(dist_with_point_key<Ht, Ht, true>(net, self, x0, x1, beta, on, on),
|
|||
|
|
self, verifiable_tree_prefix<Ht, Ht, u64>(x0, x1));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_half_tree_seed_tamper(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
using Ht = prg::aes128_ccr;
|
|||
|
|
const Input alpha = 7;
|
|||
|
|
const Input x0 = 0x25;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const u64 beta = 9;
|
|||
|
|
auto on = [&](auto key) {
|
|||
|
|
if (self == role::p0)
|
|||
|
|
flip_seeds(key);
|
|||
|
|
std::vector<proof_token> pis(256);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
proof_token pi{};
|
|||
|
|
(void)*eval_point(key, static_cast<Input>(x), prove(pi));
|
|||
|
|
pis[x] = pi;
|
|||
|
|
}
|
|||
|
|
auto peer = exchange_proofs(net, self, pis);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
int failed = 0;
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
if (!verify(pis[x], peer[x]))
|
|||
|
|
++failed;
|
|||
|
|
require(failed > 0, "seed tamper invisible");
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
require_tree_prefix(dist_with_point_key<Ht, Ht, true>(net, self, x0, x1, beta, on, on),
|
|||
|
|
self, verifiable_tree_prefix<Ht, Ht, u64>(x0, x1));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_word_tamper(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x2a;
|
|||
|
|
const Input x0 = 0x10;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const u64 beta = 5;
|
|||
|
|
auto on = [&](auto key) {
|
|||
|
|
if (self == role::p0)
|
|||
|
|
flip_words(key);
|
|||
|
|
std::vector<u64> shares(256);
|
|||
|
|
auto [bufs, iters] = eval_full(key);
|
|||
|
|
(void)bufs;
|
|||
|
|
auto it = std::begin(iters);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x, ++it)
|
|||
|
|
shares[x] = share_bits(*it);
|
|||
|
|
auto peer = exchange_u64(net, self, shares);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
int failed = 0;
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const u64 expect = x == alpha ? beta : 0u;
|
|||
|
|
if (shares[x] - peer[x] != expect)
|
|||
|
|
++failed;
|
|||
|
|
}
|
|||
|
|
require(failed > 0, "word tamper invisible");
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
require_tree_prefix(dist_with_point_key<prg::aes128, prg::aes128, true>(net, self, x0, x1, beta, on, on),
|
|||
|
|
self, verifiable_tree_prefix<prg::aes128, prg::aes128, u64>(x0, x1));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_cmp_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x40;
|
|||
|
|
const Input x0 = 0x19;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
auto on = [&](const auto & key) {
|
|||
|
|
const u64 mask = key.cmp().mask;
|
|||
|
|
std::vector<u64> shares(256);
|
|||
|
|
std::vector<proof_token> pis(256);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
shares[x] = share_bits(eval_point(
|
|||
|
|
cmp, key, static_cast<Input>(x), prove(pis[x])));
|
|||
|
|
}
|
|||
|
|
auto peer_s = exchange_u64(net, self, shares);
|
|||
|
|
auto peer_p = exchange_proofs(net, self, pis);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
require(((shares[x] + peer_s[x]) & mask)
|
|||
|
|
== (x < alpha ? 1u : 0u),
|
|||
|
|
"cmp domain value");
|
|||
|
|
require(verify(pis[x], peer_p[x]), "cmp domain proof");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
dist_with_cmp_key(net, self, x0, x1, lt(u64{1}), on, on, verifiable{});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_blocked_domain(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);
|
|||
|
|
auto on = [&](const auto & key) {
|
|||
|
|
const u64 mask = key.cmp().mask;
|
|||
|
|
std::vector<u64> shares(256);
|
|||
|
|
std::vector<proof_token> pis(256);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
shares[x] = share_bits(eval_point(
|
|||
|
|
cmp, key, static_cast<Input>(x), prove(pis[x])));
|
|||
|
|
}
|
|||
|
|
auto peer_s = exchange_u64(net, self, shares);
|
|||
|
|
auto peer_p = exchange_proofs(net, self, pis);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
require(((shares[x] + peer_s[x]) & mask)
|
|||
|
|
== (x < alpha ? 1u : 0u),
|
|||
|
|
"blocked value");
|
|||
|
|
require(verify(pis[x], peer_p[x]), "blocked proof");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
dist_with_cmp_key(net, self, x0, x1,
|
|||
|
|
block_width<4>(lt(u64{1})), on, on, verifiable{});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_multipoint_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const std::vector<Input> alphas{1, 9, 40, 255};
|
|||
|
|
const std::vector<u64> betas{7, 11, 3, 1};
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto keys = make_multipoint(alphas, betas, verifiable{});
|
|||
|
|
std::vector<u64> s0(256), s1(256);
|
|||
|
|
std::vector<proof_token> p0(256), p1(256);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const Input q = static_cast<Input>(x);
|
|||
|
|
s0[x] = share_bits(eval_multipoint(keys.first, q, prove(p0[x])));
|
|||
|
|
s1[x] = share_bits(eval_multipoint(keys.second, q, prove(p1[x])));
|
|||
|
|
}
|
|||
|
|
net.to(role::p0).send_vec(s0);
|
|||
|
|
net.to(role::p1).send_vec(s1);
|
|||
|
|
send_proofs(net.to(role::p0), p0);
|
|||
|
|
send_proofs(net.to(role::p1), p1);
|
|||
|
|
|
|||
|
|
require(!keys.first.buckets.empty(), "multipoint buckets");
|
|||
|
|
for (auto & bucket : keys.first.buckets)
|
|||
|
|
flip_seeds(bucket);
|
|||
|
|
proof_token a0{}, a1{};
|
|||
|
|
audit_multipoint(keys.first, prove(a0));
|
|||
|
|
audit_multipoint(keys.second, prove(a1));
|
|||
|
|
require(!verify(a0, a1), "dealer audit");
|
|||
|
|
send_proofs(net.to(role::p0), std::vector<proof_token>{a0});
|
|||
|
|
send_proofs(net.to(role::p1), std::vector<proof_token>{a1});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
auto shares = net.to(role::p2).recv_vec<u64>();
|
|||
|
|
auto proofs = recv_proofs(net.to(role::p2), 256);
|
|||
|
|
auto peer_s = exchange_u64(net, self, shares);
|
|||
|
|
auto peer_p = exchange_proofs(net, self, proofs);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
u64 want = 0;
|
|||
|
|
for (std::size_t i = 0; i < alphas.size(); ++i)
|
|||
|
|
if (alphas[i] == static_cast<Input>(x))
|
|||
|
|
want = betas[i];
|
|||
|
|
require(shares[x] - peer_s[x] == want, "multipoint value");
|
|||
|
|
require(verify(proofs[x], peer_p[x]), "multipoint proof");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
auto mine_audit = recv_proofs(net.to(role::p2), 1);
|
|||
|
|
auto peer_audit = exchange_proofs(net, self, mine_audit);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
require(!verify(mine_audit[0], peer_audit[0]), "bucket seed tamper");
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_fp61_mac(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
const fp61 y{20};
|
|||
|
|
const fp61 scale{2};
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto key = sample_mac_key<fp61>();
|
|||
|
|
auto shares = mac_share_value(y, key);
|
|||
|
|
std::vector<u64> a{
|
|||
|
|
shares.first.value.raw(), shares.first.tag.raw(), key.delta.raw()};
|
|||
|
|
std::vector<u64> b{
|
|||
|
|
shares.second.value.raw(), shares.second.tag.raw(), key.delta.raw()};
|
|||
|
|
net.to(role::p0).send_vec(a);
|
|||
|
|
net.to(role::p1).send_vec(b);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
auto mine = net.to(role::p2).recv_vec<u64>();
|
|||
|
|
require(mine.size() == 3u, "mac wire");
|
|||
|
|
mac_share<fp61> local{fp61{mine[0]}, fp61{mine[1]}};
|
|||
|
|
mac_key<fp61> key{fp61{mine[2]}};
|
|||
|
|
std::vector<u64> body{local.value.raw(), local.tag.raw()};
|
|||
|
|
auto peer_body = exchange_u64(net, self, body);
|
|||
|
|
mac_share<fp61> peer{fp61{peer_body[0]}, fp61{peer_body[1]}};
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(mac_verify(local, peer, key), "honest mac");
|
|||
|
|
auto opened = local.value + peer.value;
|
|||
|
|
require(opened == y, "opened y");
|
|||
|
|
auto scaled0 = mac_scale(local, scale);
|
|||
|
|
auto scaled1 = mac_scale(peer, scale);
|
|||
|
|
require(mac_verify(scaled0, scaled1, key), "scaled mac");
|
|||
|
|
require(scaled0.value + scaled1.value == fp61{40}, "scaled open");
|
|||
|
|
}
|
|||
|
|
if (self == role::p0)
|
|||
|
|
local.value = local.value + fp61{1};
|
|||
|
|
body = {local.value.raw(), local.tag.raw()};
|
|||
|
|
peer_body = exchange_u64(net, self, body);
|
|||
|
|
peer = mac_share<fp61>{fp61{peer_body[0]}, fp61{peer_body[1]}};
|
|||
|
|
if (self == role::p0)
|
|||
|
|
require(!mac_verify(local, peer, key), "value tamper");
|
|||
|
|
|
|||
|
|
local = mac_share<fp61>{fp61{mine[0]}, fp61{mine[1]}};
|
|||
|
|
if (self == role::p0)
|
|||
|
|
local.tag = local.tag + fp61{1};
|
|||
|
|
body = {local.value.raw(), local.tag.raw()};
|
|||
|
|
peer_body = exchange_u64(net, self, body);
|
|||
|
|
peer = mac_share<fp61>{fp61{peer_body[0]}, fp61{peer_body[1]}};
|
|||
|
|
if (self == role::p0)
|
|||
|
|
require(!mac_verify(local, peer, key), "tag tamper");
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_offset_poly(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
const std::uint8_t center = 2;
|
|||
|
|
const std::size_t degree = 2;
|
|||
|
|
const std::uint8_t eta = 5;
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto mat = grotto::make_offset_poly_keys<std::uint8_t>(center, degree, verifiable{});
|
|||
|
|
const std::vector<std::uint64_t> coeff{1, 0, 3};
|
|||
|
|
const std::vector<std::uint8_t> knots{0};
|
|||
|
|
std::vector<proof_token> t0(degree + 1), t1(degree + 1);
|
|||
|
|
const u64 s0 = grotto::offset_poly_eval<0>(mat, knots, {coeff}, eta, t0.data());
|
|||
|
|
const u64 s1 = grotto::offset_poly_eval<1>(mat, knots, {coeff}, eta, t1.data());
|
|||
|
|
const u64 clear = grotto::offset_poly_clear<std::uint8_t>(center, knots, {coeff}, eta);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, s0);
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, s1);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, clear);
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, clear);
|
|||
|
|
send_proofs(net.to(role::p0), t0);
|
|||
|
|
send_proofs(net.to(role::p1), t1);
|
|||
|
|
t0[1][0] = simde_mm_xor_si128(t0[1][0], simde_mm_set1_epi8(1));
|
|||
|
|
send_proofs(net.to(role::p0), t0);
|
|||
|
|
send_proofs(net.to(role::p1), t1);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
const u64 mine = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
const u64 clear = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
auto proofs = recv_proofs(net.to(role::p2), degree + 1);
|
|||
|
|
const u64 peer = open_additive(net, self, mine);
|
|||
|
|
auto peer_p = exchange_proofs(net, self, proofs);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(peer == clear, "offset poly");
|
|||
|
|
for (std::size_t m = 0; m <= degree; ++m)
|
|||
|
|
require(verify(proofs[m], peer_p[m]), "offset proof");
|
|||
|
|
}
|
|||
|
|
auto bad = recv_proofs(net.to(role::p2), degree + 1);
|
|||
|
|
auto bad_peer = exchange_proofs(net, self, bad);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(verify(bad[0], bad_peer[0]), "untampered power");
|
|||
|
|
require(!verify(bad[1], bad_peer[1]), "tampered power");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_offset_jet(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
const std::uint8_t center = 9;
|
|||
|
|
const std::size_t degree = 2;
|
|||
|
|
const std::uint8_t eta = 4;
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto mat = grotto::make_offset_jet_keys<std::uint8_t>(center, degree, verifiable{});
|
|||
|
|
const std::vector<std::uint64_t> coeff{2, 3, 1};
|
|||
|
|
const std::vector<std::uint8_t> knots{0};
|
|||
|
|
std::vector<proof_token> t0(degree + 1), t1(degree + 1);
|
|||
|
|
const u64 s0 = grotto::offset_jet_eval<0>(mat, knots, coeff, eta, t0.data());
|
|||
|
|
const u64 s1 = grotto::offset_jet_eval<1>(mat, knots, coeff, eta, t1.data());
|
|||
|
|
const u64 clear = grotto::offset_jet_clear<std::uint8_t>(center, knots, coeff, eta);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, s0);
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, s1);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, clear);
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, clear);
|
|||
|
|
send_proofs(net.to(role::p0), t0);
|
|||
|
|
send_proofs(net.to(role::p1), t1);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
const u64 mine = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
const u64 clear = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
auto proofs = recv_proofs(net.to(role::p2), degree + 1);
|
|||
|
|
const u64 peer = open_additive(net, self, mine);
|
|||
|
|
auto peer_p = exchange_proofs(net, self, proofs);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(peer == clear, "offset jet");
|
|||
|
|
for (std::size_t m = 0; m <= degree; ++m)
|
|||
|
|
require(verify(proofs[m], peer_p[m]), "jet proof");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_ring_switch(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Z = grotto::zn64<1009>;
|
|||
|
|
const std::uint8_t r = 200;
|
|||
|
|
const std::uint8_t eta = 100;
|
|||
|
|
const std::uint8_t x = static_cast<std::uint8_t>(r + eta);
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto mat = grotto::make_ring_switch_keys<Z>(r, verifiable{});
|
|||
|
|
proof_token t0{}, t1{};
|
|||
|
|
const Z s0 = grotto::ring_switch_eval<0>(mat, eta, &t0);
|
|||
|
|
const Z s1 = grotto::ring_switch_eval<1>(mat, eta, &t1);
|
|||
|
|
const Z clear = grotto::ring_switch_clear<Z>(x, r, eta);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, s0.raw());
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, s1.raw());
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, clear.raw());
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, clear.raw());
|
|||
|
|
send_proofs(net.to(role::p0), std::vector<proof_token>{t0});
|
|||
|
|
send_proofs(net.to(role::p1), std::vector<proof_token>{t1});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
const u64 mine = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
const u64 clear = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
auto proofs = recv_proofs(net.to(role::p2), 1);
|
|||
|
|
const u64 peer = open_additive(net, self, mine);
|
|||
|
|
auto peer_p = exchange_proofs(net, self, proofs);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(Z{peer} == Z{clear}, "ring switch");
|
|||
|
|
require(verify(proofs[0], peer_p[0]), "ring proof");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_offset_repr(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
const std::uint8_t center = 10;
|
|||
|
|
const std::uint8_t eta = 5;
|
|||
|
|
const auto state = grotto::offset_repr_fibonacci_state(center);
|
|||
|
|
const auto M = grotto::offset_repr_fibonacci_matrix();
|
|||
|
|
const std::size_t dim = 2;
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto mat = grotto::make_offset_repr_keys<std::uint8_t>(
|
|||
|
|
center, state, verifiable{});
|
|||
|
|
const std::vector<std::uint8_t> knots{0};
|
|||
|
|
std::vector<proof_token> t0(dim), t1(dim);
|
|||
|
|
const auto s0 = grotto::offset_repr_eval<0>(mat, M, knots, eta, t0.data());
|
|||
|
|
const auto s1 = grotto::offset_repr_eval<1>(mat, M, knots, eta, t1.data());
|
|||
|
|
const auto clear = grotto::offset_repr_clear(center, state, M, knots, eta);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, s0[1]);
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, s1[1]);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, clear[1]);
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, clear[1]);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, s0[0]);
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, s1[0]);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, clear[0]);
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, clear[0]);
|
|||
|
|
send_proofs(net.to(role::p0), t0);
|
|||
|
|
send_proofs(net.to(role::p1), t1);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
const u64 mine_fn = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
const u64 clear_fn = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
const u64 mine_fn1 = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
const u64 clear_fn1 = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
auto proofs = recv_proofs(net.to(role::p2), dim);
|
|||
|
|
const u64 peer_fn = open_additive(net, self, mine_fn);
|
|||
|
|
const u64 peer_fn1 = open_additive(net, self, mine_fn1);
|
|||
|
|
auto peer_p = exchange_proofs(net, self, proofs);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(peer_fn == clear_fn, "offset repr F_n");
|
|||
|
|
require(peer_fn1 == clear_fn1, "offset repr F_{n+1}");
|
|||
|
|
for (std::size_t i = 0; i < dim; ++i)
|
|||
|
|
require(verify(proofs[i], peer_p[i]), "repr proof");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_offset_twist(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
const std::uint8_t center = 9;
|
|||
|
|
const std::size_t degree = 2;
|
|||
|
|
const std::uint8_t eta = 4;
|
|||
|
|
const u64 lambda = 3;
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto mat = grotto::make_offset_twist_keys<std::uint8_t>(
|
|||
|
|
center, degree, lambda, verifiable{});
|
|||
|
|
const std::vector<u64> coeff{2, 5, 1};
|
|||
|
|
const std::vector<std::uint8_t> knots{0};
|
|||
|
|
std::vector<proof_token> t0(degree + 1), t1(degree + 1);
|
|||
|
|
const u64 s0 = grotto::offset_twist_eval<0>(mat, knots, coeff, eta, t0.data());
|
|||
|
|
const u64 s1 = grotto::offset_twist_eval<1>(mat, knots, coeff, eta, t1.data());
|
|||
|
|
const u64 clear = grotto::offset_twist_clear(
|
|||
|
|
center, lambda, knots, coeff, eta);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, s0);
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, s1);
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, clear);
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, clear);
|
|||
|
|
send_proofs(net.to(role::p0), t0);
|
|||
|
|
send_proofs(net.to(role::p1), t1);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
const u64 mine = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
const u64 clear = net.to(role::p2).recv<u64>(net::msg::ring_vector);
|
|||
|
|
auto proofs = recv_proofs(net.to(role::p2), degree + 1);
|
|||
|
|
const u64 peer = open_additive(net, self, mine);
|
|||
|
|
auto peer_p = exchange_proofs(net, self, proofs);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(peer == clear, "offset twist");
|
|||
|
|
for (std::size_t m = 0; m <= degree; ++m)
|
|||
|
|
require(verify(proofs[m], peer_p[m]), "twist proof");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_closed_form(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
const unsigned bits = 16;
|
|||
|
|
const std::int64_t raw = std::int64_t{1} << bits;
|
|||
|
|
const std::int64_t soft = grotto::eval_closed(grotto::closed::softsign, bits, raw);
|
|||
|
|
const std::int64_t selu = grotto::eval_closed(grotto::closed::selu, bits, -raw);
|
|||
|
|
bool bad_precision = false;
|
|||
|
|
bool pole = false;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
(void)grotto::eval_closed(grotto::closed::atan, 7, raw);
|
|||
|
|
}
|
|||
|
|
catch (const std::invalid_argument &)
|
|||
|
|
{
|
|||
|
|
bad_precision = true;
|
|||
|
|
}
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
(void)grotto::eval_closed(grotto::closed::acsch, bits, 0);
|
|||
|
|
}
|
|||
|
|
catch (const std::domain_error &)
|
|||
|
|
{
|
|||
|
|
pole = true;
|
|||
|
|
}
|
|||
|
|
require(bad_precision && pole, "closed form rejects");
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, static_cast<u64>(soft));
|
|||
|
|
net.to(role::p0).send(net::msg::ring_vector, static_cast<u64>(selu));
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, static_cast<u64>(soft));
|
|||
|
|
net.to(role::p1).send(net::msg::ring_vector, static_cast<u64>(selu));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
const auto peer_soft = static_cast<std::int64_t>(
|
|||
|
|
net.to(role::p2).recv<u64>(net::msg::ring_vector));
|
|||
|
|
const auto peer_selu = static_cast<std::int64_t>(
|
|||
|
|
net.to(role::p2).recv<u64>(net::msg::ring_vector));
|
|||
|
|
require(soft == peer_soft && selu == peer_selu, "closed form agree");
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_exact_steps(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
const unsigned bits = 16;
|
|||
|
|
const std::int64_t width = grotto::eval_dec_width(std::int64_t{3} << bits, bits);
|
|||
|
|
const std::int64_t log16 = grotto::eval_ilog16<std::int64_t>(0, bits);
|
|||
|
|
const std::int64_t angle = grotto::eval_deg2rad(std::int64_t{180} << bits, bits);
|
|||
|
|
bool wide = false;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
(void)grotto::eval_dec_width(1, 63);
|
|||
|
|
}
|
|||
|
|
catch (const std::invalid_argument &)
|
|||
|
|
{
|
|||
|
|
wide = true;
|
|||
|
|
}
|
|||
|
|
require(wide, "exact width");
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
std::vector<u64> pack{
|
|||
|
|
static_cast<u64>(width), static_cast<u64>(log16), static_cast<u64>(angle)};
|
|||
|
|
net.to(role::p0).send_vec(pack);
|
|||
|
|
net.to(role::p1).send_vec(pack);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
auto pack = net.to(role::p2).recv_vec<u64>();
|
|||
|
|
require(pack.size() == 3u, "exact pack");
|
|||
|
|
require(static_cast<u64>(width) == pack[0], "dec width");
|
|||
|
|
require(static_cast<u64>(log16) == pack[1], "ilog16");
|
|||
|
|
require(static_cast<u64>(angle) == pack[2], "deg2rad");
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_ic_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input r = 40, p = 7, q = 90;
|
|||
|
|
const Input r0 = 0x13;
|
|||
|
|
const Input r1 = static_cast<Input>(r ^ r0);
|
|||
|
|
const std::uint32_t if_true = 11, if_false = 2;
|
|||
|
|
auto on = [&](const auto & key) {
|
|||
|
|
const u64 nmask = key.input_mask;
|
|||
|
|
const u64 gmask = key.group_mask;
|
|||
|
|
std::vector<u64> shares(256);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
shares[x] =
|
|||
|
|
share_bits(eval_point(ic, key, static_cast<Input>(x)));
|
|||
|
|
auto peer = exchange_u64(net, self, shares);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
auto dealer = make_dpf(r, ic(p, q, if_true, if_false));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const u64 w = (x - static_cast<unsigned>(r)) & nmask;
|
|||
|
|
const bool inside = w >= p && w <= q;
|
|||
|
|
const u64 want = (inside ? if_true : if_false) & gmask;
|
|||
|
|
const u64 got = (shares[x] + peer[x]) & gmask;
|
|||
|
|
require(got == want, "ic");
|
|||
|
|
const u64 d0 = share_bits(
|
|||
|
|
eval_point(ic, dealer.first, static_cast<Input>(x)));
|
|||
|
|
const u64 d1 = share_bits(
|
|||
|
|
eval_point(ic, dealer.second, static_cast<Input>(x)));
|
|||
|
|
require(((d0 + d1) & gmask) == got, "ic matches dealer");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
// Default path keeps mask `r` shared (F_IC); still matches a dealer key.
|
|||
|
|
dist_with_ic_key(net, self, r0, r1, ic(p, q, if_true, if_false), on, on);
|
|||
|
|
// Opt-in Reveal reconstructs `r`.
|
|||
|
|
require_opened(dist_with_ic_key<prg::aes128, prg::aes128, true>(net, self,
|
|||
|
|
r0, r1, ic(p, q, if_true, if_false), on, on),
|
|||
|
|
self, utils::xor_input_shares(r0, r1));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_cmp_edge_default(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
// Domain-edge point for leq/gt: α = 2^n-1. Default path must not open the
|
|||
|
|
// edge bit, and the key must still evaluate correctly.
|
|||
|
|
const Input alpha = 0xff;
|
|||
|
|
const Input x0 = 0x19;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
auto check = [&](auto kind, auto pred) {
|
|||
|
|
auto on = [&](const auto & key) {
|
|||
|
|
const u64 mask = key.cmp().mask;
|
|||
|
|
require(key.cmp().trivial == cmp_trivial::none, "edge trivial unset");
|
|||
|
|
std::vector<u64> shares(256);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
shares[x] = share_bits(
|
|||
|
|
eval_point(cmp, key, static_cast<Input>(x)));
|
|||
|
|
auto peer = exchange_u64(net, self, shares);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const u64 want = pred(x) ? 1u : 0u;
|
|||
|
|
require(((shares[x] + peer[x]) & mask) == want, "edge cmp");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
dist_with_cmp_key(net, self, x0, x1, kind, on, on);
|
|||
|
|
};
|
|||
|
|
check(leq(u64{1}), [](unsigned x) { return x <= 255u; });
|
|||
|
|
check(gt(u64{1}), [](unsigned x) { return false; });
|
|||
|
|
// Non-edge leq/gt also stay on the default (no Reveal) path.
|
|||
|
|
const Input mid = 0x40;
|
|||
|
|
const Input m0 = 0x11;
|
|||
|
|
const Input m1 = static_cast<Input>(mid ^ m0);
|
|||
|
|
auto on_mid = [&](const auto & key) {
|
|||
|
|
const u64 mask = key.cmp().mask;
|
|||
|
|
std::vector<u64> shares(256);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
shares[x] = share_bits(eval_point(cmp, key, static_cast<Input>(x)));
|
|||
|
|
auto peer = exchange_u64(net, self, shares);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
require(((shares[x] + peer[x]) & mask)
|
|||
|
|
== (x <= mid ? 1u : 0u),
|
|||
|
|
"leq mid");
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
dist_with_cmp_key(net, self, m0, m1, leq(u64{1}), on_mid, on_mid);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_point_default(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x3c;
|
|||
|
|
const Input x0 = 0x10;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const u64 beta = 0x55;
|
|||
|
|
auto on = [&](const auto & key) {
|
|||
|
|
std::vector<u64> mine(256);
|
|||
|
|
auto [bufs, iters] = eval_full(key);
|
|||
|
|
(void)bufs;
|
|||
|
|
auto it = std::begin(iters);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x, ++it)
|
|||
|
|
mine[x] = share_bits(*it);
|
|||
|
|
auto peer = exchange_u64(net, self, mine);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const u64 got = mine[x] - peer[x];
|
|||
|
|
require(got == (x == alpha ? beta : 0u), "point default");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
// Default path returns nothing (prefix stays hidden).
|
|||
|
|
dist_with_point_key(net, self, x0, x1, beta, on, on);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_geneval_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x3c;
|
|||
|
|
const Input x0 = 0x10;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const Input y = 0x7e;
|
|||
|
|
auto on = [&](const auto & key) {
|
|||
|
|
std::vector<Input> mine(256);
|
|||
|
|
std::vector<proof_token> pis(256);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
mine[x] = share_bits(
|
|||
|
|
*eval_point(key, static_cast<Input>(x), prove(pis[x])));
|
|||
|
|
}
|
|||
|
|
auto peer = exchange_u8(net, self, mine);
|
|||
|
|
auto peer_p = exchange_proofs(net, self, pis);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(mine.size() == 256u, "geneval shares");
|
|||
|
|
require(static_cast<Input>(mine[alpha] - peer[alpha]) == y, "geneval on");
|
|||
|
|
require(static_cast<Input>(mine[0] - peer[0]) == Input{0}, "geneval off");
|
|||
|
|
require(static_cast<Input>(peer[alpha] - mine[alpha]) != y, "party order");
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
require(verify(pis[x], peer_p[x]), "geneval proof");
|
|||
|
|
require(!verify(detail::vdpf::zero_proof(), peer_p[alpha]),
|
|||
|
|
"zero token must fail");
|
|||
|
|
require(!verify(pis[alpha], detail::vdpf::zero_proof()),
|
|||
|
|
"zero peer token must fail");
|
|||
|
|
auto flipped = peer_p[alpha];
|
|||
|
|
flipped[0] = simde_mm_xor_si128(flipped[0], simde_mm_set1_epi8(1));
|
|||
|
|
require(!verify(pis[alpha], flipped), "tampered token");
|
|||
|
|
}
|
|||
|
|
auto corrupted = key;
|
|||
|
|
if (self == role::p0)
|
|||
|
|
flip_words(corrupted);
|
|||
|
|
proof_token bad_pi{};
|
|||
|
|
const Input bad = open_subtractive(net, self,
|
|||
|
|
share_bits(*eval_point(corrupted, alpha, prove(bad_pi))));
|
|||
|
|
auto peer_bad = exchange_proof(net, self, bad_pi);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(bad != y, "dist word tamper");
|
|||
|
|
require(!verify(bad_pi, peer_bad), "dist proof after word tamper");
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
require_tree_prefix(dist_with_point_key<prg::aes128, prg::aes128, true>(net, self, x0, x1, y, on, on),
|
|||
|
|
self, verifiable_tree_prefix<prg::aes128, prg::aes128, decltype(y)>(x0, x1));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_dist_half_tree_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
using Ht = prg::aes128_ccr;
|
|||
|
|
const Input alpha = 9;
|
|||
|
|
const Input x0 = 0x3;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const u64 beta = 0x1111;
|
|||
|
|
auto on = [&](const auto & key) {
|
|||
|
|
int hits = 0;
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const u64 opened = open_subtractive(net, self,
|
|||
|
|
share_bits(*eval_point(key, static_cast<Input>(x))));
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(opened == (x == alpha ? beta : 0u), "dist half-tree");
|
|||
|
|
if (opened == beta)
|
|||
|
|
++hits;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (self == role::p0)
|
|||
|
|
require(hits == 1, "dist half-tree hits");
|
|||
|
|
};
|
|||
|
|
require_tree_prefix(dist_with_point_key<Ht, Ht, true>(net, self, x0, x1, beta, on, on),
|
|||
|
|
self, verifiable_tree_prefix<Ht, Ht, u64>(x0, x1));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_dist_packed_leaf(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
using Out = std::uint16_t;
|
|||
|
|
const Input alpha = 0x2a;
|
|||
|
|
const Input x0 = 0x11;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const Out beta = 0x1234;
|
|||
|
|
const Input neighbor = static_cast<Input>(alpha ^ 0x1);
|
|||
|
|
auto on = [&](const auto & key) {
|
|||
|
|
const Out on_v = open_subtractive(net, self,
|
|||
|
|
share_bits(*eval_point(key, alpha)));
|
|||
|
|
const Out lane = open_subtractive(net, self,
|
|||
|
|
share_bits(*eval_point(key, neighbor)));
|
|||
|
|
const Out far = open_subtractive(net, self,
|
|||
|
|
share_bits(*eval_point(key, Input{0})));
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
require(on_v == beta, "packed on");
|
|||
|
|
require(lane == Out{0}, "packed neighbor");
|
|||
|
|
require(far == Out{0}, "packed far");
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
require_tree_prefix(dist_with_point_key<prg::aes128, prg::aes128, true>(net, self, x0, x1, beta, on, on),
|
|||
|
|
self, verifiable_tree_prefix<prg::aes128, prg::aes128, Out>(x0, x1));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_extractable_second_hot(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x2a;
|
|||
|
|
const Input x0 = 0x10;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const fp61 beta{7};
|
|||
|
|
auto on = [&](const auto & key) {
|
|||
|
|
const std::array<fp61, 2> rs{fp61{3}, fp61{5}};
|
|||
|
|
sketch_share local{};
|
|||
|
|
auto sk = sketch(local, rs);
|
|||
|
|
(void)*eval_point(key, Input{0}, sk);
|
|||
|
|
(void)*eval_point(key, alpha, sk);
|
|||
|
|
role peer = self == role::p0 ? role::p1 : role::p0;
|
|||
|
|
sketch_share theirs =
|
|||
|
|
net.exchange_with(peer, local, net::msg::sketch_share);
|
|||
|
|
bool honest = self == role::p0 ? sketch_verify(local, theirs)
|
|||
|
|
: sketch_verify(theirs, local);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
require(honest, "honest sketch");
|
|||
|
|
|
|||
|
|
sketch_share forged{};
|
|||
|
|
auto bad = sketch(forged, rs);
|
|||
|
|
fp61 y0 = (*eval_point(key, Input{0})).raw();
|
|||
|
|
const fp61 y1 = (*eval_point(key, alpha)).raw();
|
|||
|
|
if (self == role::p0)
|
|||
|
|
y0 = y0 + beta;
|
|||
|
|
bad.absorb(y0);
|
|||
|
|
bad.absorb(y1);
|
|||
|
|
sketch_share peer_forged =
|
|||
|
|
net.exchange_with(peer, forged, net::msg::sketch_share);
|
|||
|
|
bool second = self == role::p0
|
|||
|
|
? sketch_verify(forged, peer_forged)
|
|||
|
|
: sketch_verify(peer_forged, forged);
|
|||
|
|
if (self == role::p0)
|
|||
|
|
require(!second, "second hot point");
|
|||
|
|
};
|
|||
|
|
dist_with_extractable_point_key(
|
|||
|
|
net, self, x0, x1, beta, on, on);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------------------------------------------------------------------------
|
|||
|
|
// Three-evaluator (2,3) DPF.
|
|||
|
|
// Dealer flows: p2 runs make_dpf3* and ships keys (α clear to dealer).
|
|||
|
|
// Dist flows: dist_with_dpf3_key (α XOR-shared; role map dpf3_role_map::dist).
|
|||
|
|
// ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
/// @brief Collect three Shamir shares at p2 and reconstruct.
|
|||
|
|
/// @details Party indices follow `dpf3_party_of(role, map)`. Only p2 returns
|
|||
|
|
/// the opened value; p0/p1 return zero.
|
|||
|
|
fp61 open_shamir3(trio & net, role self, fp61 mine,
|
|||
|
|
dpf3_role_map map = dpf3_role_map::dealer)
|
|||
|
|
{
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
const auto from_p0 = net.to(role::p0).recv<fp61>(net::msg::delta);
|
|||
|
|
const auto from_p1 = net.to(role::p1).recv<fp61>(net::msg::delta);
|
|||
|
|
return shamir3::reconstruct(
|
|||
|
|
shamir3::share{dpf3_party_of(role::p0, map), from_p0},
|
|||
|
|
shamir3::share{dpf3_party_of(role::p1, map), from_p1},
|
|||
|
|
shamir3::share{dpf3_party_of(role::p2, map), mine});
|
|||
|
|
}
|
|||
|
|
net.to(role::p2).send(net::msg::delta, mine);
|
|||
|
|
return fp61{};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Open F_DPF3CMP complementary halves at p2 (dealer role map).
|
|||
|
|
/// @details p0 holds the k0 half, p1 the k1 half. p2's `mine` is unused for
|
|||
|
|
/// the open (party 3 also holds k0) and may be a dummy.
|
|||
|
|
fp61 open_cmp3(trio & net, role self, std::uint64_t mine)
|
|||
|
|
{
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
const auto k0 = net.to(role::p0).recv<std::uint64_t>(net::msg::delta);
|
|||
|
|
const auto k1 = net.to(role::p1).recv<std::uint64_t>(net::msg::delta);
|
|||
|
|
(void)mine;
|
|||
|
|
return reconstruct_cmp_halves(k0, k1);
|
|||
|
|
}
|
|||
|
|
net.to(role::p2).send(net::msg::delta, mine);
|
|||
|
|
return fp61{};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_dpf3_point_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x2a;
|
|||
|
|
const fp61 beta{17};
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto [k1, k2, k3] = make_dpf3(alpha, beta, verifiable{});
|
|||
|
|
send_key(net.to(role::p0), k1);
|
|||
|
|
send_key(net.to(role::p1), k2);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const fp61 y = eval_point(k3, static_cast<Input>(x));
|
|||
|
|
const fp61 got = open_shamir3(net, self, y);
|
|||
|
|
require(got == (static_cast<Input>(x) == alpha ? beta : fp61{}),
|
|||
|
|
"dpf3 point");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
using K = std::decay_t<decltype(std::get<0>(
|
|||
|
|
make_dpf3(Input{}, fp61{}, verifiable{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_shamir3(net, self, eval_point(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
using K = std::decay_t<decltype(std::get<1>(
|
|||
|
|
make_dpf3(Input{}, fp61{}, verifiable{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_shamir3(net, self, eval_point(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_dpf3_proof_fail(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x11;
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto [k1, k2, k3] = make_dpf3(alpha, fp61{3}, verifiable{});
|
|||
|
|
using arr = typename decltype(k1.a.dpf_key)::correction_seeds_array;
|
|||
|
|
for (auto & cs : const_cast<arr &>(k1.a.dpf_key.correction_seeds()))
|
|||
|
|
cs[0] = simde_mm_xor_si128(cs[0], simde_mm_set1_epi8(1));
|
|||
|
|
auto p1 = prove_dpf3(k1, alpha);
|
|||
|
|
auto p2 = prove_dpf3(k2, alpha);
|
|||
|
|
auto p3 = prove_dpf3(k3, alpha);
|
|||
|
|
require(!verify_dpf3(p1, p2, p3), "dpf3 proof fail");
|
|||
|
|
net.to(role::p0).send(net::msg::delta, std::uint8_t{1});
|
|||
|
|
net.to(role::p1).send(net::msg::delta, std::uint8_t{1});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
(void)net.to(role::p2).recv<std::uint8_t>(net::msg::delta);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_dpf3_update(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x07;
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto [k1, k2, k3] = make_dpf3(alpha, fp61{5}, updatable{});
|
|||
|
|
update_payload(k1, k2, k3, alpha, fp61{9});
|
|||
|
|
send_key(net.to(role::p0), k1);
|
|||
|
|
send_key(net.to(role::p1), k2);
|
|||
|
|
const fp61 y = eval_point(k3, alpha);
|
|||
|
|
const fp61 got = open_shamir3(net, self, y);
|
|||
|
|
require(got == fp61{9}, "dpf3 update");
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
using K = std::decay_t<decltype(std::get<0>(
|
|||
|
|
make_dpf3(Input{}, fp61{}, updatable{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
(void)open_shamir3(net, self, eval_point(key, alpha));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
using K = std::decay_t<decltype(std::get<1>(
|
|||
|
|
make_dpf3(Input{}, fp61{}, updatable{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
(void)open_shamir3(net, self, eval_point(key, alpha));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_dpf3_cmp_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input thresh = 100;
|
|||
|
|
const u64 beta = 5;
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto [k1, k2, k3] = make_dpf3_cmp(thresh, beta);
|
|||
|
|
send_key(net.to(role::p0), k1);
|
|||
|
|
send_key(net.to(role::p1), k2);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const auto y = eval_point(k3, static_cast<Input>(x));
|
|||
|
|
const fp61 got = open_cmp3(net, self, y);
|
|||
|
|
require(got.raw() == (x < thresh ? beta : 0u), "dpf3 cmp");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
using K = std::decay_t<decltype(std::get<0>(make_dpf3_cmp(Input{}, u64{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_cmp3(net, self, eval_point(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
using K = std::decay_t<decltype(std::get<1>(make_dpf3_cmp(Input{}, u64{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_cmp3(net, self, eval_point(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_dist_dpf3_point(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{17};
|
|||
|
|
constexpr auto map = dpf3_role_map::dist;
|
|||
|
|
require(x0 != alpha && x1 != alpha, "dist dpf3: alpha shares only");
|
|||
|
|
const auto opened = dist_with_dpf3_key(net, self, x0, x1, beta,
|
|||
|
|
[&](auto key) {
|
|||
|
|
// Party 1 (p0): eval is a Shamir share, not clear β.
|
|||
|
|
require(eval_point(key, alpha) != beta,
|
|||
|
|
"dist dpf3: p0 beta hidden");
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_shamir3(net, self,
|
|||
|
|
eval_point(key, static_cast<Input>(x)), map);
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const fp61 got = open_shamir3(net, self,
|
|||
|
|
eval_point(key, static_cast<Input>(x)), map);
|
|||
|
|
require(got == (static_cast<Input>(x) == alpha ? beta : fp61{}),
|
|||
|
|
"dist dpf3 point");
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
// Party 3 (p1): only τ halves were received; eval ≠ clear β.
|
|||
|
|
require(eval_point(key, alpha) != beta,
|
|||
|
|
"dist dpf3: p1 beta is share only");
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_shamir3(net, self,
|
|||
|
|
eval_point(key, static_cast<Input>(x)), map);
|
|||
|
|
});
|
|||
|
|
require(!opened.has_value(), "dist dpf3: no clear prefix");
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Dist updatable keys: Fig-10 over the wire, then open at `α`.
|
|||
|
|
int recent_dist_dpf3_update(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x07;
|
|||
|
|
const Input x0 = 0x03;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const fp61 beta0{5};
|
|||
|
|
const fp61 beta1{9};
|
|||
|
|
constexpr auto map = dpf3_role_map::dist;
|
|||
|
|
dist_with_dpf3_key(net, self, x0, x1, beta0, updatable{},
|
|||
|
|
[&](auto key) {
|
|||
|
|
require(key.updatable, "dist dpf3 update: p0 updatable");
|
|||
|
|
dist_update_payload(net, self, key, alpha, beta1);
|
|||
|
|
(void)open_shamir3(net, self, eval_point(key, alpha), map);
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
require(key.updatable, "dist dpf3 update: p2 updatable");
|
|||
|
|
dist_update_payload(net, self, key, alpha, beta1);
|
|||
|
|
const fp61 got =
|
|||
|
|
open_shamir3(net, self, eval_point(key, alpha), map);
|
|||
|
|
require(got == beta1, "dist dpf3 update");
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
require(key.updatable, "dist dpf3 update: p1 updatable");
|
|||
|
|
dist_update_payload(net, self, key, alpha, beta1);
|
|||
|
|
(void)open_shamir3(net, self, eval_point(key, alpha), map);
|
|||
|
|
});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Dist dual-spine keys: prove at `α` and verify on p2 (party 2).
|
|||
|
|
int recent_dist_dpf3_proof(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x19;
|
|||
|
|
const Input x0 = 0x07;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const fp61 beta{4};
|
|||
|
|
dist_with_dpf3_key(net, self, x0, x1, beta,
|
|||
|
|
[&](auto key) {
|
|||
|
|
require(key.verifiable, "dist dpf3 proof: p0 verifiable");
|
|||
|
|
const auto p = prove_dpf3(key, alpha);
|
|||
|
|
net.to(role::p2).send(net::msg::delta, p);
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
require(key.verifiable, "dist dpf3 proof: p2 verifiable");
|
|||
|
|
const auto p1 = net.to(role::p0).recv<dpf3_proof>(net::msg::delta);
|
|||
|
|
const auto p3 = net.to(role::p1).recv<dpf3_proof>(net::msg::delta);
|
|||
|
|
const auto p2 = prove_dpf3(key, alpha);
|
|||
|
|
require(verify_dpf3(p1, p2, p3), "dist dpf3 proof ok");
|
|||
|
|
// Corrupt party-1 A-half token; verify must fail closed.
|
|||
|
|
dpf3_proof bad = p1;
|
|||
|
|
bad.a[0] = simde_mm_xor_si128(bad.a[0], simde_mm_set1_epi8(1));
|
|||
|
|
require(!verify_dpf3(bad, p2, p3), "dist dpf3 proof fail");
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
require(key.verifiable, "dist dpf3 proof: p1 verifiable");
|
|||
|
|
const auto p = prove_dpf3(key, alpha);
|
|||
|
|
net.to(role::p2).send(net::msg::delta, p);
|
|||
|
|
});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_dpf3_ic_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input r = 10, p = 20, q = 40;
|
|||
|
|
const u64 beta = 3;
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto [k1, k2, k3] = make_dpf3_ic(r, p, q, beta);
|
|||
|
|
auto two = make_dpf(r, ic(p, q, beta));
|
|||
|
|
send_key(net.to(role::p0), k1);
|
|||
|
|
send_key(net.to(role::p1), k2);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const auto want = reconstruct(
|
|||
|
|
eval_point(ic, two.first, static_cast<Input>(x)),
|
|||
|
|
eval_point(ic, two.second, static_cast<Input>(x)));
|
|||
|
|
const auto y = eval_point(k3, static_cast<Input>(x));
|
|||
|
|
const fp61 got = open_cmp3(net, self, y);
|
|||
|
|
require(got.raw() == static_cast<u64>(want), "dpf3 ic");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
using K = std::decay_t<decltype(std::get<0>(
|
|||
|
|
make_dpf3_ic(Input{}, Input{}, Input{}, u64{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_cmp3(net, self, eval_point(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
using K = std::decay_t<decltype(std::get<1>(
|
|||
|
|
make_dpf3_ic(Input{}, Input{}, Input{}, u64{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_cmp3(net, self, eval_point(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Dealer cmp update (fp61 delta) then full-domain open on all three.
|
|||
|
|
int recent_dpf3_cmp_update(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input thresh = 80;
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto [k1, k2, k3] = make_dpf3_cmp(thresh, 11u);
|
|||
|
|
update_payload_cmp(k1, k2, k3, 11u, 0u);
|
|||
|
|
send_key(net.to(role::p0), k1);
|
|||
|
|
send_key(net.to(role::p1), k2);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const fp61 got =
|
|||
|
|
open_cmp3(net, self, eval_point(k3, static_cast<Input>(x)));
|
|||
|
|
require(got.raw() == 0u, "dpf3 cmp update clear");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
using K = std::decay_t<decltype(std::get<0>(make_dpf3_cmp(Input{}, u64{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_cmp3(net, self, eval_point(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
using K = std::decay_t<decltype(std::get<1>(make_dpf3_cmp(Input{}, u64{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_cmp3(net, self, eval_point(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Dealer blocked comparison full domain.
|
|||
|
|
int recent_dpf3_blocked_cmp(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input thresh = 50;
|
|||
|
|
const u64 beta = 9;
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto [k1, k2, k3] = make_dpf3_cmp_blocked<4>(thresh, beta);
|
|||
|
|
send_key(net.to(role::p0), k1);
|
|||
|
|
send_key(net.to(role::p1), k2);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const fp61 got =
|
|||
|
|
open_cmp3(net, self, eval_point(k3, static_cast<Input>(x)));
|
|||
|
|
require(got.raw() == (x < thresh ? beta : 0u), "dpf3 blocked cmp");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
using K = std::decay_t<decltype(std::get<0>(
|
|||
|
|
make_dpf3_cmp_blocked<4>(Input{}, u64{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_cmp3(net, self, eval_point(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
using K = std::decay_t<decltype(std::get<1>(
|
|||
|
|
make_dpf3_cmp_blocked<4>(Input{}, u64{})))>;
|
|||
|
|
auto key = recv_key<K>(net.to(role::p2));
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_cmp3(net, self, eval_point(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Dealer multipoint3 full domain on all three evaluators.
|
|||
|
|
/// @details `multipoint3_key` embeds a `std::vector` of buckets; ship σ/meta
|
|||
|
|
/// then each bucket via `send_key` (not a flat memcpy of the parent).
|
|||
|
|
int recent_dpf3_multipoint_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const std::vector<Input> alphas{1, 2, 9, 40};
|
|||
|
|
const std::vector<fp61> betas{fp61{7}, fp61{11}, fp61{3}, fp61{4}};
|
|||
|
|
using K1 = std::decay_t<decltype(std::get<0>(
|
|||
|
|
make_multipoint3(alphas, betas, verifiable{})))>;
|
|||
|
|
using K2 = std::decay_t<decltype(std::get<1>(
|
|||
|
|
make_multipoint3(alphas, betas, verifiable{})))>;
|
|||
|
|
using Bucket1 = typename K1::bucket_key;
|
|||
|
|
using Bucket2 = typename K2::bucket_key;
|
|||
|
|
|
|||
|
|
auto send_mp = [&](role peer, const auto & key) {
|
|||
|
|
net.to(peer).send(net::msg::delta, key.sigma);
|
|||
|
|
net.to(peer).send(net::msg::delta, key.bucket_count);
|
|||
|
|
net.to(peer).send(net::msg::delta, key.bucket_domain);
|
|||
|
|
const std::uint64_t nb = key.buckets.size();
|
|||
|
|
net.to(peer).send(net::msg::delta, nb);
|
|||
|
|
for (const auto & b : key.buckets)
|
|||
|
|
send_key(net.to(peer), b);
|
|||
|
|
};
|
|||
|
|
auto recv_mp = [&](auto empty_key) {
|
|||
|
|
using Key = decltype(empty_key);
|
|||
|
|
Key key = std::move(empty_key);
|
|||
|
|
key.sigma = net.to(role::p2).template recv<simde__m128i>(net::msg::delta);
|
|||
|
|
key.bucket_count =
|
|||
|
|
net.to(role::p2).template recv<std::uint64_t>(net::msg::delta);
|
|||
|
|
key.bucket_domain =
|
|||
|
|
net.to(role::p2).template recv<mpf_word>(net::msg::delta);
|
|||
|
|
const auto nb =
|
|||
|
|
net.to(role::p2).template recv<std::uint64_t>(net::msg::delta);
|
|||
|
|
key.buckets.clear();
|
|||
|
|
key.buckets.reserve(static_cast<std::size_t>(nb));
|
|||
|
|
using Bucket = typename Key::bucket_key;
|
|||
|
|
for (std::uint64_t i = 0; i < nb; ++i)
|
|||
|
|
key.buckets.push_back(recv_key<Bucket>(net.to(role::p2)));
|
|||
|
|
key.verifiable = true;
|
|||
|
|
return key;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
auto [k1, k2, k3] = make_multipoint3(alphas, betas, verifiable{});
|
|||
|
|
send_mp(role::p0, k1);
|
|||
|
|
send_mp(role::p1, k2);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
fp61 want{};
|
|||
|
|
for (std::size_t i = 0; i < alphas.size(); ++i)
|
|||
|
|
if (alphas[i] == static_cast<Input>(x))
|
|||
|
|
want = betas[i];
|
|||
|
|
const fp61 got = open_shamir3(net, self,
|
|||
|
|
eval_multipoint(k3, static_cast<Input>(x)));
|
|||
|
|
require(got == want, "dpf3 multipoint");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
if (self == role::p0)
|
|||
|
|
{
|
|||
|
|
K1 empty{};
|
|||
|
|
auto key = recv_mp(std::move(empty));
|
|||
|
|
(void)sizeof(Bucket1);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_shamir3(net, self,
|
|||
|
|
eval_multipoint(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
K2 empty{};
|
|||
|
|
auto key = recv_mp(std::move(empty));
|
|||
|
|
(void)sizeof(Bucket2);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_shamir3(net, self,
|
|||
|
|
eval_multipoint(key, static_cast<Input>(x)));
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Dist Fig-10 then full-domain open (not only α).
|
|||
|
|
int recent_dist_dpf3_update_domain(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x2b;
|
|||
|
|
const Input x0 = 0x05;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const fp61 beta0{3};
|
|||
|
|
const fp61 beta1{13};
|
|||
|
|
constexpr auto map = dpf3_role_map::dist;
|
|||
|
|
dist_with_dpf3_key(net, self, x0, x1, beta0, updatable{},
|
|||
|
|
[&](auto key) {
|
|||
|
|
dist_update_payload(net, self, key, alpha, beta1);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_shamir3(net, self,
|
|||
|
|
eval_point(key, static_cast<Input>(x)), map);
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
dist_update_payload(net, self, key, alpha, beta1);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
{
|
|||
|
|
const fp61 got = open_shamir3(net, self,
|
|||
|
|
eval_point(key, static_cast<Input>(x)), map);
|
|||
|
|
require(got == (static_cast<Input>(x) == alpha ? beta1 : fp61{}),
|
|||
|
|
"dist dpf3 update domain");
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
dist_update_payload(net, self, key, alpha, beta1);
|
|||
|
|
for (unsigned x = 0; x < 256; ++x)
|
|||
|
|
(void)open_shamir3(net, self,
|
|||
|
|
eval_point(key, static_cast<Input>(x)), map);
|
|||
|
|
});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Dist update then prove/verify still succeeds on all three parties.
|
|||
|
|
int recent_dist_dpf3_update_proof(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x1c;
|
|||
|
|
const Input x0 = 0x09;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
dist_with_dpf3_key(net, self, x0, x1, fp61{2}, updatable{},
|
|||
|
|
[&](auto key) {
|
|||
|
|
dist_update_payload(net, self, key, alpha, fp61{8});
|
|||
|
|
require(key.verifiable, "dist update proof: p0 V");
|
|||
|
|
net.to(role::p2).send(net::msg::delta, prove_dpf3(key, alpha));
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
dist_update_payload(net, self, key, alpha, fp61{8});
|
|||
|
|
const auto p1 = net.to(role::p0).recv<dpf3_proof>(net::msg::delta);
|
|||
|
|
const auto p3 = net.to(role::p1).recv<dpf3_proof>(net::msg::delta);
|
|||
|
|
const auto p2 = prove_dpf3(key, alpha);
|
|||
|
|
require(verify_dpf3(p1, p2, p3), "dist update proof ok");
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
dist_update_payload(net, self, key, alpha, fp61{8});
|
|||
|
|
net.to(role::p2).send(net::msg::delta, prove_dpf3(key, alpha));
|
|||
|
|
});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Dist shares opened under the dealer role map must not reconstruct.
|
|||
|
|
int recent_dist_dpf3_wrong_map(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using Input = std::uint8_t;
|
|||
|
|
const Input alpha = 0x33;
|
|||
|
|
const Input x0 = 0x0a;
|
|||
|
|
const Input x1 = static_cast<Input>(alpha ^ x0);
|
|||
|
|
const fp61 beta{6};
|
|||
|
|
dist_with_dpf3_key(net, self, x0, x1, beta,
|
|||
|
|
[&](auto key) {
|
|||
|
|
(void)open_shamir3(net, self, eval_point(key, alpha),
|
|||
|
|
dpf3_role_map::dealer);
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
bool rejected = false;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
const fp61 got = open_shamir3(net, self, eval_point(key, alpha),
|
|||
|
|
dpf3_role_map::dealer);
|
|||
|
|
rejected = got != beta;
|
|||
|
|
}
|
|||
|
|
catch (const std::runtime_error &)
|
|||
|
|
{
|
|||
|
|
rejected = true;
|
|||
|
|
}
|
|||
|
|
require(rejected, "dealer map on dist shares");
|
|||
|
|
},
|
|||
|
|
[&](auto key) {
|
|||
|
|
(void)open_shamir3(net, self, eval_point(key, alpha),
|
|||
|
|
dpf3_role_map::dealer);
|
|||
|
|
});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Static role-map contract used by open_shamir3 (p0/p1/p2 all check).
|
|||
|
|
int recent_dpf3_role_map(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
(void)net;
|
|||
|
|
require(dpf3_party_of(role::p0, dpf3_role_map::dealer) == 1, "dealer p0");
|
|||
|
|
require(dpf3_party_of(role::p1, dpf3_role_map::dealer) == 2, "dealer p1");
|
|||
|
|
require(dpf3_party_of(role::p2, dpf3_role_map::dealer) == 3, "dealer p2");
|
|||
|
|
require(dpf3_party_of(role::p0, dpf3_role_map::dist) == 1, "dist p0");
|
|||
|
|
require(dpf3_party_of(role::p2, dpf3_role_map::dist) == 2, "dist p2");
|
|||
|
|
require(dpf3_party_of(role::p1, dpf3_role_map::dist) == 3, "dist p1");
|
|||
|
|
// Cross-wire check: dealer indices ≠ dist for p1/p2.
|
|||
|
|
require(dpf3_party_of(role::p1, dpf3_role_map::dealer)
|
|||
|
|
!= dpf3_party_of(role::p1, dpf3_role_map::dist),
|
|||
|
|
"p1 map differs");
|
|||
|
|
require(dpf3_party_of(role::p2, dpf3_role_map::dealer)
|
|||
|
|
!= dpf3_party_of(role::p2, dpf3_role_map::dist),
|
|||
|
|
"p2 map differs");
|
|||
|
|
(void)self;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// @brief Oblivious correction-seed hash matches in-process `make_cs`.
|
|||
|
|
/// @details One level of the shared AES circuit. Prefix shares are split so
|
|||
|
|
/// neither party holds the clear prefix, and the seeds differ.
|
|||
|
|
int recent_oblivious_cs_matches(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
auto block_from = [](std::uint64_t hi, std::uint64_t lo) {
|
|||
|
|
const std::uint64_t limbs[2] = {lo, hi};
|
|||
|
|
simde__m128i v;
|
|||
|
|
std::memcpy(&v, limbs, sizeof(v));
|
|||
|
|
return v;
|
|||
|
|
};
|
|||
|
|
const simde__m128i s0 = block_from(0x1111222233334444ULL, 0x5555666677778888ULL);
|
|||
|
|
const simde__m128i s1 = block_from(0x0102030405060708ULL, 0x89abcdef00112233ULL);
|
|||
|
|
struct case_t
|
|||
|
|
{
|
|||
|
|
std::size_t level;
|
|||
|
|
std::uint64_t prefix;
|
|||
|
|
std::uint64_t share0;
|
|||
|
|
};
|
|||
|
|
const case_t cases[] = {
|
|||
|
|
{0, 0, 0},
|
|||
|
|
{1, 1, 0},
|
|||
|
|
{3, 0x2a, 0x11},
|
|||
|
|
{7, 0xff, 0x5a},
|
|||
|
|
{dpf::detail::blocked::fold_spine_tag | 2, 0x15, 0x01},
|
|||
|
|
};
|
|||
|
|
for (const auto & c : cases)
|
|||
|
|
{
|
|||
|
|
if (self == role::p2)
|
|||
|
|
{
|
|||
|
|
dist::send_hash_tape(net);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
auto tape = net.to(role::p2).template recv_vec<dist::bit_and_pad_msg>(
|
|||
|
|
dpf::net::msg::beaver_tape);
|
|||
|
|
require(tape.size() == dist::hash_level_and_count(), "hash tape");
|
|||
|
|
const std::uint64_t share = self == role::p0
|
|||
|
|
? c.share0 : (c.prefix ^ c.share0);
|
|||
|
|
const simde__m128i seed = self == role::p0 ? s0 : s1;
|
|||
|
|
dpf::cs_block got{};
|
|||
|
|
if (self == role::p0)
|
|||
|
|
got = dist::oblivious_cs<0>(net, c.level, share, seed, tape.data());
|
|||
|
|
else
|
|||
|
|
got = dist::oblivious_cs<1>(net, c.level, share, seed, tape.data());
|
|||
|
|
const auto expect = dpf::detail::vdpf::make_cs(
|
|||
|
|
c.level, c.prefix, s0, s1);
|
|||
|
|
require(std::memcmp(got.data(), expect.data(), sizeof(got)) == 0,
|
|||
|
|
"oblivious cs");
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int recent_grow_extend(role self, trio & net)
|
|||
|
|
{
|
|||
|
|
using In = std::uint8_t;
|
|||
|
|
const In alpha = 0xB2;
|
|||
|
|
const std::uint64_t beta = 9;
|
|||
|
|
dist_with_grow_extend(net, self, alpha, beta,
|
|||
|
|
[&](const auto & key) {
|
|||
|
|
require(std::decay_t<decltype(key)>::depth == 1, "grown depth");
|
|||
|
|
require(std::decay_t<decltype(key)>::num_outputs == 2, "grown outs");
|
|||
|
|
(void)key;
|
|||
|
|
},
|
|||
|
|
[&](const auto & key) {
|
|||
|
|
require(std::decay_t<decltype(key)>::depth == 1, "grown depth");
|
|||
|
|
(void)key;
|
|||
|
|
});
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#define REG(name, tags, fn) \
|
|||
|
|
register_flow(flow{#name, tags, fn, false})
|
|||
|
|
|
|||
|
|
} // namespace recent
|
|||
|
|
|
|||
|
|
void register_recent_flows()
|
|||
|
|
{
|
|||
|
|
using namespace recent;
|
|||
|
|
REG(recent_half_tree_domain, "recent verifiable half-tree", recent_half_tree_domain);
|
|||
|
|
REG(recent_half_tree_seed_tamper, "recent verifiable half-tree", recent_half_tree_seed_tamper);
|
|||
|
|
REG(recent_word_tamper, "recent verifiable", recent_word_tamper);
|
|||
|
|
REG(recent_cmp_domain, "recent verifiable dcf", recent_cmp_domain);
|
|||
|
|
REG(recent_blocked_domain, "recent verifiable dcf", recent_blocked_domain);
|
|||
|
|
REG(recent_multipoint_domain, "recent verifiable multipoint", recent_multipoint_domain);
|
|||
|
|
REG(recent_fp61_mac, "recent mac", recent_fp61_mac);
|
|||
|
|
REG(recent_offset_poly, "recent grotto verifiable", recent_offset_poly);
|
|||
|
|
REG(recent_offset_jet, "recent grotto verifiable", recent_offset_jet);
|
|||
|
|
REG(recent_ring_switch, "recent grotto verifiable", recent_ring_switch);
|
|||
|
|
REG(recent_offset_repr, "recent grotto verifiable", recent_offset_repr);
|
|||
|
|
REG(recent_offset_twist, "recent grotto verifiable", recent_offset_twist);
|
|||
|
|
REG(recent_closed_form, "recent grotto", recent_closed_form);
|
|||
|
|
REG(recent_exact_steps, "recent grotto", recent_exact_steps);
|
|||
|
|
REG(recent_ic_domain, "recent ic", recent_ic_domain);
|
|||
|
|
REG(recent_cmp_edge_default, "recent dcf", recent_cmp_edge_default);
|
|||
|
|
REG(recent_point_default, "recent dist", recent_point_default);
|
|||
|
|
REG(recent_geneval_domain, "recent dist", recent_geneval_domain);
|
|||
|
|
REG(recent_dist_half_tree_domain, "recent dist half-tree", recent_dist_half_tree_domain);
|
|||
|
|
REG(recent_dist_packed_leaf, "recent dist", recent_dist_packed_leaf);
|
|||
|
|
REG(recent_extractable_second_hot, "recent extractable", recent_extractable_second_hot);
|
|||
|
|
REG(recent_dpf3_point_domain, "recent dpf3 dealer", recent_dpf3_point_domain);
|
|||
|
|
REG(recent_dpf3_proof_fail, "recent dpf3 dealer", recent_dpf3_proof_fail);
|
|||
|
|
REG(recent_dpf3_update, "recent dpf3 dealer", recent_dpf3_update);
|
|||
|
|
REG(recent_dpf3_cmp_domain, "recent dpf3 dealer", recent_dpf3_cmp_domain);
|
|||
|
|
REG(recent_dpf3_ic_domain, "recent dpf3 dealer", recent_dpf3_ic_domain);
|
|||
|
|
REG(recent_dpf3_cmp_update, "recent dpf3 dealer", recent_dpf3_cmp_update);
|
|||
|
|
REG(recent_dpf3_blocked_cmp, "recent dpf3 dealer", recent_dpf3_blocked_cmp);
|
|||
|
|
REG(recent_dpf3_multipoint_domain, "recent dpf3 dealer", recent_dpf3_multipoint_domain);
|
|||
|
|
REG(recent_dpf3_role_map, "recent dpf3 dealer", recent_dpf3_role_map);
|
|||
|
|
REG(recent_dist_dpf3_point, "recent dpf3 dist", recent_dist_dpf3_point);
|
|||
|
|
REG(recent_dist_dpf3_proof, "recent dpf3 dist", recent_dist_dpf3_proof);
|
|||
|
|
REG(recent_dist_dpf3_update, "recent dpf3 dist", recent_dist_dpf3_update);
|
|||
|
|
REG(recent_dist_dpf3_update_domain, "recent dpf3 dist", recent_dist_dpf3_update_domain);
|
|||
|
|
REG(recent_dist_dpf3_update_proof, "recent dpf3 dist", recent_dist_dpf3_update_proof);
|
|||
|
|
REG(recent_dist_dpf3_wrong_map, "recent dpf3 dist", recent_dist_dpf3_wrong_map);
|
|||
|
|
REG(recent_oblivious_cs_matches, "recent verifiable hash", recent_oblivious_cs_matches);
|
|||
|
|
REG(recent_grow_extend, "recent grow ds", recent_grow_extend);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#undef REG
|
|||
|
|
|
|||
|
|
} // namespace party
|
|||
|
|
} // namespace dpf
|