libdpf/include/dpf/interval.hpp

674 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

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

/// @file dpf/interval.hpp
/// @brief Public-bound interval containment on one comparison key.
/// @details `make_dpf(r, ic(p, q, β))` hides the mask `r` and the payload `β`.
/// The bounds are public. Reconstruction is `β` when
/// `p ≤ (x − r) mod 2^n ≤ q`, and the false payload otherwise.
///
/// The key is one `lt` comparison at `γ = r − 1`, the Boyle–Chandran–
/// Gilboa–Gupta–Ishai–Kumar–Rathee reduction (EUROCRYPT 2021, Fig. 3).
/// Evaluation walks that key at the two public shifts of `x` and adds
/// a secret-shared correction. Seed corrections, advice bits, leaves,
/// and the path memoizer stay single-path.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
#ifndef LIBDPF_INCLUDE_DPF_INTERVAL_HPP__
#define LIBDPF_INCLUDE_DPF_INTERVAL_HPP__
#include <cstddef>
#include <cstdint>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "hedley/hedley.h"
#include "dpf/dcf.hpp"
#include "dpf/eval_unified.hpp"
#include "dpf/geneval.hpp"
#include "dpf/incremental.hpp"
#include "dpf/output_buffer.hpp"
#include "dpf/secret_share.hpp"
#include "dpf/utils.hpp"
namespace dpf
{
template <typename Beta>
struct ic_pack
{
static constexpr bool is_ic = true;
using beta_type = Beta;
uint64_t lo = 0;
uint64_t hi = 0;
Beta if_true{};
Beta if_false{};
};
/// Spec tag and factory. `dpf::ic(p, q, beta)` builds a pack;
/// `eval_point(dpf::ic, key, x)` evaluates it.
struct ic_fn
{
template <typename Lo, typename Hi, typename Beta>
HEDLEY_WARN_UNUSED_RESULT
ic_pack<std::decay_t<Beta>> operator()(Lo lo, Hi hi, Beta t,
Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>()) const
{
ic_pack<std::decay_t<Beta>> spec;
spec.lo = static_cast<uint64_t>(lo);
spec.hi = static_cast<uint64_t>(hi);
spec.if_true = std::move(t);
spec.if_false = std::move(f);
return spec;
}
};
inline constexpr ic_fn ic{};
template <typename T>
struct is_ic_key : std::false_type {};
template <std::size_t Party, typename Key, typename Input, typename Beta>
struct ic_key
{
static constexpr std::size_t party = Party;
static constexpr bool wildcard = Key::cmp_is_wildcard;
using input_type = Input;
using key_type = party_key<Party, Key>;
using beta_type = Beta;
key_type key;
uint64_t lo = 0;
uint64_t hi = 0;
uint64_t input_mask = 0;
uint64_t group_mask = 0;
/// Share of `δ`. Public `c_x ∈ {-1,0,1}` scales it locally.
uint64_t delta_share = 0;
/// Share of `δ · c_r + if_false`.
uint64_t cr_share = 0;
/// Wildcard only: shares of `1` and of `c_r`, scaled by `δ` in `assign_cmp`.
uint64_t delta_coeff = 0;
uint64_t cr_coeff = 0;
bool assigned = !wildcard;
ic_key(key_type k, uint64_t lo_in, uint64_t hi_in, uint64_t nmask,
uint64_t gmask, uint64_t dshare, uint64_t cshare, uint64_t dcoeff,
uint64_t ccoeff)
: key(std::move(k))
, lo(lo_in)
, hi(hi_in)
, input_mask(nmask)
, group_mask(gmask)
, delta_share(dshare)
, cr_share(cshare)
, delta_coeff(dcoeff)
, cr_coeff(ccoeff)
{}
};
template <std::size_t Party, typename Key, typename Input, typename Beta>
struct is_ic_key<ic_key<Party, Key, Input, Beta>> : std::true_type {};
template <typename T>
inline constexpr bool is_ic_key_v = is_ic_key<std::decay_t<T>>::value;
namespace detail
{
namespace ic_impl
{
template <typename Input>
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t input_mask_of() noexcept
{
constexpr auto n = utils::bitlength_of_v<Input>;
if constexpr (n >= 64)
return ~uint64_t{0};
else
return (uint64_t{1} << n) - 1ULL;
}
template <typename Input>
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t bits_of(Input x) noexcept
{
constexpr auto to_int = utils::to_integral_type<Input>{};
return static_cast<uint64_t>(to_int(x)) & input_mask_of<Input>();
}
template <typename Input>
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr Input input_from_bits(uint64_t u) noexcept
{
return static_cast<Input>(u & input_mask_of<Input>());
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t embed_small(int s, uint64_t mask) noexcept
{
if (s >= 0)
return static_cast<uint64_t>(s) & mask;
return dcf_impl::neg_m(static_cast<uint64_t>(-s), mask);
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t mul_mask(uint64_t a, uint64_t b, uint64_t mask) noexcept
{
return static_cast<uint64_t>(static_cast<unsigned __int128>(a) * b) & mask;
}
/// Dealer correction in Fig. 3, as an element of the payload group.
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t correction(uint64_t r, uint64_t p, uint64_t q,
uint64_t nmask, uint64_t gmask) noexcept
{
const uint64_t aq = (q + r) & nmask;
const uint64_t ap = (p + r) & nmask;
const uint64_t q0 = (q + 1ULL) & nmask;
const uint64_t aq0 = (q0 + r) & nmask;
const int s = (ap > aq ? 1 : 0) - (ap > p ? 1 : 0)
+ (aq0 > q0 ? 1 : 0) + (aq == nmask ? 1 : 0);
return embed_small(s, gmask);
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr int public_cx(uint64_t x, uint64_t p, uint64_t q, uint64_t nmask) noexcept
{
const uint64_t q0 = (q + 1ULL) & nmask;
return (x > p ? 1 : 0) - (x > q0 ? 1 : 0);
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t shift_p(uint64_t x, uint64_t p, uint64_t nmask) noexcept
{
return (x + (nmask - p)) & nmask;
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t shift_q0(uint64_t x, uint64_t q, uint64_t nmask) noexcept
{
const uint64_t q0 = (q + 1ULL) & nmask;
return (x + (nmask - q0)) & nmask;
}
template <typename T>
HEDLEY_PURE
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
uint64_t opened_u64(const T & v, uint64_t mask) noexcept
{
if constexpr (is_secret_share_v<std::decay_t<T>>)
return dcf_impl::beta_to_u64_simple(v.raw(), mask);
else
return dcf_impl::beta_to_u64_simple(v, mask);
}
inline void split_target(uint64_t target, uint64_t mask,
uint64_t & s0, uint64_t & s1)
{
const uint64_t blind = dcf_impl::sample_addend_blind(mask,
[] { return dpf::uniform_sample<simde__m128i>(); });
incr::split_cmp_addend(target, mask, blind, s0, s1);
}
template <typename Input>
void check_input()
{
static_assert(std::is_unsigned_v<Input> && !std::is_same_v<Input, bool>,
"ic: input type must be an unsigned integer of at most 64 bits");
static_assert(utils::bitlength_of_v<Input> <= 64,
"ic: input type must be an unsigned integer of at most 64 bits");
static_assert(utils::bitlength_of_v<Input> > 0,
"ic: input type must be an unsigned integer of at most 64 bits");
}
template <typename Input, typename Beta>
void check_bounds(const ic_pack<Beta> & spec)
{
const uint64_t nmask = input_mask_of<Input>();
if (spec.lo > nmask || spec.hi > nmask)
throw std::invalid_argument("ic: bound does not fit in the input domain");
if (spec.lo > spec.hi)
throw std::invalid_argument("ic: require lo <= hi (the interval does not wrap)");
}
template <typename Beta>
HEDLEY_NO_THROW
uint64_t group_mask_of() noexcept
{
using B = concrete_type_t<Beta>;
if constexpr (std::is_same_v<B, dpf::bit>)
return 1ULL;
else
return dcf_impl::default_mask_for_bits(utils::bitlength_of_v<B>);
}
template <std::size_t Party, typename Key, typename Input, typename Beta>
ic_key<Party, Key, Input, Beta> make_side(party_key<Party, Key> key,
uint64_t lo, uint64_t hi, uint64_t nmask, uint64_t gmask,
uint64_t delta_share, uint64_t cr_share,
uint64_t delta_coeff, uint64_t cr_coeff)
{
return ic_key<Party, Key, Input, Beta>(std::move(key), lo, hi, nmask, gmask,
delta_share, cr_share, delta_coeff, cr_coeff);
}
template <typename Input, typename Beta, typename Pair>
auto finish(uint64_t r_bits, const ic_pack<Beta> & spec, Pair && inner)
{
using in_type = std::decay_t<Input>;
using party0 = std::decay_t<decltype(inner.first)>;
using raw_key = typename party0::key_type;
using out_beta = concrete_type_t<Beta>;
const uint64_t nmask = input_mask_of<in_type>();
const uint64_t gmask = group_mask_of<Beta>();
constexpr bool wild = is_wildcard_v<Beta>;
uint64_t delta = 0;
uint64_t fval = 0;
if constexpr (!wild)
{
delta = dcf_impl::beta_delta_u64(spec.if_true, spec.if_false, gmask);
fval = dcf_impl::beta_to_u64_simple(spec.if_false, gmask);
}
const uint64_t cr = correction(r_bits, spec.lo, spec.hi, nmask, gmask);
uint64_t d0 = 0, d1 = 0, c0 = 0, c1 = 0;
uint64_t dc0 = 0, dc1 = 0, cc0 = 0, cc1 = 0;
if constexpr (wild)
{
split_target(1ULL & gmask, gmask, dc0, dc1);
split_target(cr, gmask, cc0, cc1);
}
else
{
split_target(delta, gmask, d0, d1);
const uint64_t absorb =
(mul_mask(delta, cr, gmask) + fval) & gmask;
split_target(absorb, gmask, c0, c1);
}
auto k0 = make_side<0, raw_key, in_type, out_beta>(std::move(inner.first),
spec.lo, spec.hi, nmask, gmask, d0, c0, dc0, cc0);
auto k1 = make_side<1, raw_key, in_type, out_beta>(std::move(inner.second),
spec.lo, spec.hi, nmask, gmask, d1, c1, dc1, cc1);
return std::make_pair(std::move(k0), std::move(k1));
}
template <typename Beta>
auto inner_lt(const ic_pack<Beta> & spec)
{
using B = std::decay_t<Beta>;
if constexpr (is_wildcard_v<B>)
return lt(spec.if_true, spec.if_false);
else
{
const uint64_t gmask = group_mask_of<B>();
const uint64_t delta =
dcf_impl::beta_delta_u64(spec.if_true, spec.if_false, gmask);
return lt(dcf_impl::u64_to_beta<B>(delta), dcf_impl::u64_to_beta<B>(0));
}
}
template <typename Input>
Input gamma_of(Input r)
{
const uint64_t nmask = input_mask_of<Input>();
const uint64_t ru = bits_of(r);
return input_from_bits<Input>((ru - 1ULL) & nmask);
}
template <typename IcKey, typename Query, typename Memo>
auto eval_one(const IcKey & k, Query && x, Memo & memo)
{
if (!k.assigned)
throw std::invalid_argument(
"ic eval: wildcard payload not assigned (call assign_cmp)");
using in_type = typename IcKey::input_type;
const uint64_t xu = bits_of(in_type(std::forward<Query>(x)));
const uint64_t xp = shift_p(xu, k.lo, k.input_mask);
const uint64_t xq = shift_q0(xu, k.hi, k.input_mask);
const uint64_t a = opened_u64(
eval_point(dpf::cmp, k.key, input_from_bits<in_type>(xp), memo),
k.group_mask);
const uint64_t b = opened_u64(
eval_point(dpf::cmp, k.key, input_from_bits<in_type>(xq), memo),
k.group_mask);
const int cx = public_cx(xu, k.lo, k.hi, k.input_mask);
uint64_t scaled = 0;
if (cx == 1)
scaled = k.delta_share & k.group_mask;
else if (cx == -1)
scaled = dcf_impl::neg_m(k.delta_share, k.group_mask);
const uint64_t y = (dcf_impl::neg_m(a, k.group_mask) + b + k.cr_share
+ scaled) & k.group_mask;
return make_eval_cmp_result<typename IcKey::key_type>(
dcf_impl::u64_to_beta<typename IcKey::beta_type>(y));
}
} // namespace ic_impl
} // namespace detail
/// Dealer key for public bounds `spec` and secret mask `r`.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf(InputT && r, const ic_pack<Beta> & spec)
{
using input_type = std::decay_t<InputT>;
detail::ic_impl::check_input<input_type>();
detail::ic_impl::check_bounds<input_type>(spec);
const uint64_t r_bits = detail::ic_impl::bits_of(input_type(r));
const input_type gamma = detail::ic_impl::gamma_of(input_type(r));
auto inner = make_dpf<InteriorPRG, ExteriorPRG>(gamma,
detail::ic_impl::inner_lt(spec));
return detail::ic_impl::finish<input_type>(r_bits, spec, std::move(inner));
}
/// Doerner–Shelat key. `r0 XOR r1` is the secret mask.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename RootSampler,
typename PadRng,
typename InputT,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf_doerner_shelat(InputT r0, InputT r1,
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
{
using input_type = std::decay_t<InputT>;
detail::ic_impl::check_input<input_type>();
detail::ic_impl::check_bounds<input_type>(spec);
const input_type r = utils::xor_input_shares(r0, r1);
const uint64_t r_bits = detail::ic_impl::bits_of(r);
const input_type gamma = detail::ic_impl::gamma_of(r);
const input_type g0 = r0;
const input_type g1 = utils::xor_input_shares(g0, gamma);
auto inner = make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(g0, g1,
std::move(rng), detail::ic_impl::inner_lt(spec));
return detail::ic_impl::finish<input_type>(r_bits, spec, std::move(inner));
}
/// Doerner–Shelat IC key. `r0 + r1` is the secret mask; γ = (r0 + r1) − 1.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename RootSampler,
typename PadRng,
typename InputT,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf_doerner_shelat(arith_input_t, InputT r0, InputT r1,
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
{
using input_type = std::decay_t<InputT>;
detail::ic_impl::check_input<input_type>();
detail::ic_impl::check_bounds<input_type>(spec);
const uint64_t nmask = detail::ic_impl::input_mask_of<input_type>();
const uint64_t r_bits =
(detail::ic_impl::bits_of(r0) + detail::ic_impl::bits_of(r1)) & nmask;
const input_type r = detail::ic_impl::input_from_bits<input_type>(r_bits);
// Additive shares of γ = r − 1: (r0 − 1, r1).
const input_type g0 = detail::ic_impl::input_from_bits<input_type>(
(detail::ic_impl::bits_of(r0) - 1ULL) & nmask);
const input_type g1 = r1;
auto inner = make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(
arith_input, g0, g1, std::move(rng), detail::ic_impl::inner_lt(spec));
return detail::ic_impl::finish<input_type>(
detail::ic_impl::bits_of(r), spec, std::move(inner));
}
/// Doerner–Shelat key sampled from the library entropy source.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf_doerner_shelat(InputT r0, InputT r1, const ic_pack<Beta> & spec)
{
using block = typename InteriorPRG::block_type;
ds_randomness<block (*)(), detail::urandom_pad_rng> rng{
dpf::uniform_sample<block>, {}};
return make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(
std::move(r0), std::move(r1), rng, spec);
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf_doerner_shelat(arith_input_t, InputT r0, InputT r1,
const ic_pack<Beta> & spec)
{
using block = typename InteriorPRG::block_type;
ds_randomness<block (*)(), detail::urandom_pad_rng> rng{
dpf::uniform_sample<block>, {}};
return make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(
arith_input, std::move(r0), std::move(r1), rng, spec);
}
/// Open a wildcard interval payload onto an existing key pair.
template <typename Key, typename Input, typename Beta, typename Payload>
void assign_cmp(ic_key<0, Key, Input, Beta> & k0,
ic_key<1, Key, Input, Beta> & k1, const Payload & if_true,
const Payload & if_false = Payload{})
{
static_assert(Key::cmp_is_wildcard,
"assign_cmp: interval payload is not a wildcard");
const uint64_t mask = k0.group_mask;
const uint64_t delta =
detail::dcf_impl::beta_delta_u64(if_true, if_false, mask);
const uint64_t fval =
detail::dcf_impl::beta_to_u64_simple(if_false, mask);
assign_cmp(k0.key, k1.key,
detail::dcf_impl::u64_to_beta<Beta>(delta),
detail::dcf_impl::u64_to_beta<Beta>(0));
k0.delta_share = detail::ic_impl::mul_mask(k0.delta_coeff, delta, mask);
k1.delta_share = detail::ic_impl::mul_mask(k1.delta_coeff, delta, mask);
k0.cr_share = detail::ic_impl::mul_mask(k0.cr_coeff, delta, mask);
k1.cr_share = detail::ic_impl::mul_mask(k1.cr_coeff, delta, mask);
uint64_t f0 = 0, f1 = 0;
detail::ic_impl::split_target(fval, mask, f0, f1);
k0.cr_share = (k0.cr_share + f0) & mask;
k1.cr_share = (k1.cr_share + f1) & mask;
k0.assigned = true;
k1.assigned = true;
}
/// Point evaluation. `memo` is a path memoizer for the inner comparison key.
template <typename IcKey, typename Query,
typename Memo = basic_path_memoizer<typename IcKey::key_type>,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
HEDLEY_WARN_UNUSED_RESULT
auto eval_point(ic_fn, const IcKey & key, Query && x, Memo && memo = Memo{})
{
return detail::ic_impl::eval_one(key, std::forward<Query>(x), memo);
}
/// Inclusive interval `[from, to]` on the input domain.
template <typename IcKey, typename Lane, typename Buffer, typename Memo,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
void eval_interval(ic_fn, const IcKey & key, Lane from, Lane to,
Buffer && buf, Memo && memo)
{
using in_type = typename IcKey::input_type;
const uint64_t nmask = key.input_mask;
const uint64_t a = detail::ic_impl::bits_of(in_type(from));
const uint64_t b = detail::ic_impl::bits_of(in_type(to));
if (a > b)
throw std::invalid_argument("ic interval: to < from");
std::size_t i = 0;
for (uint64_t x = a;; ++x)
{
buf[i++] = detail::ic_impl::eval_one(key,
detail::ic_impl::input_from_bits<in_type>(x), memo);
if (x == b)
break;
if (x == nmask)
throw std::invalid_argument("ic interval: to < from");
}
}
template <typename IcKey, typename Lane, typename Buffer,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
void eval_interval(ic_fn, const IcKey & key, Lane from, Lane to, Buffer && buf)
{
basic_path_memoizer<typename IcKey::key_type> memo;
eval_interval(ic, key, from, to, std::forward<Buffer>(buf), memo);
}
/// Evaluate the points in `[begin, end)`.
template <typename IcKey, typename Iter, typename Buffer, typename Memo,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
void eval_sequence(ic_fn, const IcKey & key, Iter begin, Iter end,
Buffer && buf, Memo && memo)
{
std::size_t i = 0;
for (auto it = begin; it != end; ++it, ++i)
buf[i] = detail::ic_impl::eval_one(key, *it, memo);
}
template <typename IcKey, typename Iter, typename Buffer,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
void eval_sequence(ic_fn, const IcKey & key, Iter begin, Iter end, Buffer && buf)
{
basic_path_memoizer<typename IcKey::key_type> memo;
eval_sequence(ic, key, begin, end, std::forward<Buffer>(buf), memo);
}
/// Buffer of `n` interval shares.
template <typename IcKey, typename = std::enable_if_t<is_ic_key_v<IcKey>>>
HEDLEY_WARN_UNUSED_RESULT
auto make_output_buffer(ic_fn, const IcKey &, std::size_t n)
{
using beta = typename IcKey::beta_type;
using elem = cmp_buffer_elem_t<typename IcKey::key_type, beta>;
return output_buffer<elem>(n);
}
/// Buffer large enough for the inclusive interval `[from, to]`.
template <typename IcKey, typename Lane,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
HEDLEY_WARN_UNUSED_RESULT
auto make_output_buffer(ic_fn, const IcKey & key, Lane from, Lane to)
{
using in_type = typename IcKey::input_type;
const uint64_t a = detail::ic_impl::bits_of(in_type(from));
const uint64_t b = detail::ic_impl::bits_of(in_type(to));
if (a > b)
throw std::invalid_argument("ic interval: to < from");
const uint64_t n = b - a + 1ULL;
return make_output_buffer(ic, key, static_cast<std::size_t>(n));
}
/// Doerner–Shelat geneval. `r0 XOR r1` is the secret mask. Each query is
/// returned already combined into the interval share.
template <typename InputT, typename Iter, typename RootSampler, typename PadRng,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
geneval_cmp_result geneval_ic(InputT r0, InputT r1, Iter begin, Iter end,
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
{
static_assert(!is_wildcard_v<Beta>,
"geneval_ic: payload must be concrete (assign_cmp is a separate step)");
geneval_cmp_result out;
if (begin == end)
return out;
auto keys = make_dpf_doerner_shelat(std::move(r0), std::move(r1),
std::move(rng), spec);
const auto & k0 = keys.first;
const auto & k1 = keys.second;
using key_type = unwrap_party_key_t<typename std::decay_t<decltype(k0)>::key_type>;
constexpr std::size_t depth = key_type::depth;
out.live_levels = depth;
out.mask = k0.key.cmp().mask;
out.cw_last = k0.key.cw_last();
out.addend0 = k0.key.cmp_addend().raw();
out.addend1 = k1.key.cmp_addend().raw();
out.correction_words.resize(depth);
out.correction_advice.resize(depth);
out.value_cw.resize(depth);
for (std::size_t level = 0; level < depth; ++level)
{
out.correction_words[level] = k0.key.correction_word(level);
out.correction_advice[level] =
static_cast<uint8_t>(k0.key.correction_advice(level));
out.value_cw[level] = k0.key.value_cw(level);
}
for (auto it = begin; it != end; ++it)
{
out.party0.push_back(detail::ic_impl::opened_u64(
eval_point(ic, k0, *it), out.mask));
out.party1.push_back(detail::ic_impl::opened_u64(
eval_point(ic, k1, *it), out.mask));
}
return out;
}
/// Additive-share geneval_ic. `r0 + r1` is the secret mask.
template <typename InputT, typename Iter, typename RootSampler, typename PadRng,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
geneval_cmp_result geneval_ic(arith_input_t, InputT r0, InputT r1, Iter begin,
Iter end, ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
{
static_assert(!is_wildcard_v<Beta>,
"geneval_ic: payload must be concrete (assign_cmp is a separate step)");
geneval_cmp_result out;
if (begin == end)
return out;
auto keys = make_dpf_doerner_shelat(arith_input, std::move(r0), std::move(r1),
std::move(rng), spec);
const auto & k0 = keys.first;
const auto & k1 = keys.second;
using key_type = unwrap_party_key_t<typename std::decay_t<decltype(k0)>::key_type>;
constexpr std::size_t depth = key_type::depth;
out.live_levels = depth;
out.mask = k0.key.cmp().mask;
out.cw_last = k0.key.cw_last();
out.addend0 = k0.key.cmp_addend().raw();
out.addend1 = k1.key.cmp_addend().raw();
out.correction_words.resize(depth);
out.correction_advice.resize(depth);
out.value_cw.resize(depth);
for (std::size_t level = 0; level < depth; ++level)
{
out.correction_words[level] = k0.key.correction_word(level);
out.correction_advice[level] =
static_cast<uint8_t>(k0.key.correction_advice(level));
out.value_cw[level] = k0.key.value_cw(level);
}
for (auto it = begin; it != end; ++it)
{
out.party0.push_back(detail::ic_impl::opened_u64(
eval_point(ic, k0, *it), out.mask));
out.party1.push_back(detail::ic_impl::opened_u64(
eval_point(ic, k1, *it), out.mask));
}
return out;
}
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_INTERVAL_HPP__