674 lines
24 KiB
C++
674 lines
24 KiB
C++
/// @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__
|