Checkpoint the party/runtime stack before share-program and malicious-mode work.
Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
695f8e84f7
commit
0d22946a0e
1835 changed files with 170291 additions and 2849 deletions
|
|
@ -5,7 +5,9 @@
|
|||
/// `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).
|
||||
/// Gilboa–Gupta–Ishai–Kumar–Rathee reduction (EUROCRYPT 2021, Fig. 3;
|
||||
/// ePrint 2020/1392). Their Section 4.1 is one DCF for a public interval,
|
||||
/// where the earlier gate used about two.
|
||||
/// 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.
|
||||
|
|
@ -26,6 +28,8 @@
|
|||
#include "dpf/dcf.hpp"
|
||||
#include "dpf/eval_unified.hpp"
|
||||
#include "dpf/geneval.hpp"
|
||||
#include "dpf/grow.hpp"
|
||||
#include "dpf/grow_ds.hpp"
|
||||
#include "dpf/incremental.hpp"
|
||||
#include "dpf/output_buffer.hpp"
|
||||
#include "dpf/secret_share.hpp"
|
||||
|
|
@ -86,7 +90,9 @@ struct ic_key
|
|||
detail::cmp_group_info<dpf::concrete_type_t<Beta>>::custom,
|
||||
detail::group_elem, uint64_t>;
|
||||
|
||||
key_type key;
|
||||
/// @brief Inner comparison key. Eval that accepts a `dpf_key` also accepts
|
||||
/// this object and reads `dpf_key`.
|
||||
key_type dpf_key;
|
||||
uint64_t lo = 0;
|
||||
uint64_t hi = 0;
|
||||
uint64_t input_mask = 0;
|
||||
|
|
@ -103,7 +109,7 @@ struct ic_key
|
|||
ic_key(key_type k, uint64_t lo_in, uint64_t hi_in, uint64_t nmask,
|
||||
uint64_t gmask, share_type dshare, share_type cshare, share_type dcoeff,
|
||||
share_type ccoeff) noexcept(std::is_nothrow_move_constructible_v<key_type>)
|
||||
: key(std::move(k))
|
||||
: dpf_key(std::move(k))
|
||||
, lo(lo_in)
|
||||
, hi(hi_in)
|
||||
, input_mask(nmask)
|
||||
|
|
@ -417,7 +423,8 @@ Input gamma_of(Input r)
|
|||
}
|
||||
|
||||
template <typename IcKey, typename Query, typename Memo>
|
||||
auto eval_one(const IcKey & k, Query && x, Memo & memo)
|
||||
auto eval_one(const IcKey & k, Query && x, Memo & memo,
|
||||
proof_token * pi = nullptr)
|
||||
{
|
||||
if (!k.assigned)
|
||||
throw std::invalid_argument(
|
||||
|
|
@ -435,10 +442,10 @@ auto eval_one(const IcKey & k, Query && x, Memo & memo)
|
|||
else
|
||||
return detail::group_from_beta(v);
|
||||
};
|
||||
const auto a = opened(eval_point<beta>(dpf::cmp, k.key,
|
||||
input_from_bits<in_type>(xp), memo));
|
||||
const auto b = opened(eval_point<beta>(dpf::cmp, k.key,
|
||||
input_from_bits<in_type>(xq), memo));
|
||||
const auto a = opened(detail::incr::eval_cmp_point_impl<beta>(k.dpf_key,
|
||||
input_from_bits<in_type>(xp), memo, pi));
|
||||
const auto b = opened(detail::incr::eval_cmp_point_impl<beta>(k.dpf_key,
|
||||
input_from_bits<in_type>(xq), memo, pi));
|
||||
const int cx = public_cx(xu, k.lo, k.hi, k.input_mask);
|
||||
auto scaled = detail::group_zero(a);
|
||||
if (cx == 1)
|
||||
|
|
@ -453,10 +460,12 @@ auto eval_one(const IcKey & k, Query && x, Memo & memo)
|
|||
else
|
||||
{
|
||||
const uint64_t a = opened_u64(
|
||||
eval_point(dpf::cmp, k.key, input_from_bits<in_type>(xp), memo),
|
||||
detail::incr::eval_cmp_point_impl(k.dpf_key,
|
||||
input_from_bits<in_type>(xp), memo, pi),
|
||||
k.group_mask);
|
||||
const uint64_t b = opened_u64(
|
||||
eval_point(dpf::cmp, k.key, input_from_bits<in_type>(xq), memo),
|
||||
detail::incr::eval_cmp_point_impl(k.dpf_key,
|
||||
input_from_bits<in_type>(xq), memo, pi),
|
||||
k.group_mask);
|
||||
const int cx = public_cx(xu, k.lo, k.hi, k.input_mask);
|
||||
uint64_t scaled = 0;
|
||||
|
|
@ -482,6 +491,8 @@ auto eval_one(const IcKey & k, Query && x, Memo & memo)
|
|||
/// @param r the secret input mask
|
||||
/// @param spec the public bounds and payloads
|
||||
/// @return Dealer key for public bounds `spec` and secret mask `r`
|
||||
/// @note Following Boyle, Chandran, Gilboa, Gupta, Ishai, Kumar, and Rathee, EUROCRYPT 2021, Fig. 3 (ePrint 2020/1392): one comparison key, evaluated at two public shifts.
|
||||
/// \complexity O(n) time and O(n) key size. n is the domain bitlength (`depth`). The loop does two interior PRG expansions and writes one correction word per level, then builds one exterior leaf per output.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
|
|
@ -516,16 +527,23 @@ auto make_dpf(InputT && r, const ic_pack<Beta> & spec)
|
|||
/// @param r1 party 1's share of the mask
|
||||
/// @param rng the Doerner–Shelat randomness tapes
|
||||
/// @param spec the public bounds and payloads
|
||||
/// @param tags optional `verifiable` or `extractable` markers
|
||||
/// @return the two party keys
|
||||
/// \complexity O(n) time. One `ds_advance_level` per level: two PRG expansions and one `prepare_level`. n is `depth`.
|
||||
/// \rounds No sockets. This is the in-process transcript. A networked walk is `dpf::party::dist::point_party`.
|
||||
/// \communication none here. `local_cw_protocol` opens the correction word locally.
|
||||
/// \preprocessing Per level, `prepare_level` draws one `ds_cw_pads` (two parties × a 128-bit rand, a 128-bit gamma, and a bit) and two `ds_and_pads`. Arithmetic inputs also run a carry chain of n-1 bit-AND triples in `encode_walk_shares`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename RootSampler,
|
||||
typename PadRng,
|
||||
typename InputT,
|
||||
typename Beta>
|
||||
typename Beta,
|
||||
typename ...Tags>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto make_dpf_doerner_shelat(InputT r0, InputT r1,
|
||||
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
|
||||
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec,
|
||||
Tags && ...tags)
|
||||
{
|
||||
using input_type = std::decay_t<InputT>;
|
||||
detail::ic_impl::check_input<input_type>();
|
||||
|
|
@ -536,7 +554,8 @@ auto make_dpf_doerner_shelat(InputT r0, InputT r1,
|
|||
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));
|
||||
std::move(rng), detail::ic_impl::inner_lt(spec),
|
||||
std::forward<Tags>(tags)...);
|
||||
return detail::ic_impl::finish<input_type>(r_bits, spec, std::move(inner));
|
||||
}
|
||||
|
||||
|
|
@ -547,16 +566,23 @@ auto make_dpf_doerner_shelat(InputT r0, InputT r1,
|
|||
/// @param r1 party 1's share of the mask
|
||||
/// @param rng the Doerner–Shelat randomness tapes
|
||||
/// @param spec the public bounds and payloads
|
||||
/// @param tags optional `verifiable` or `extractable` markers
|
||||
/// @return the two party keys
|
||||
/// \complexity O(n) time. One `ds_advance_level` per level: two PRG expansions and one `prepare_level`. n is `depth`.
|
||||
/// \rounds No sockets. This is the in-process transcript. A networked walk is `dpf::party::dist::point_party`.
|
||||
/// \communication none here. `local_cw_protocol` opens the correction word locally.
|
||||
/// \preprocessing Per level, `prepare_level` draws one `ds_cw_pads` (two parties × a 128-bit rand, a 128-bit gamma, and a bit) and two `ds_and_pads`. Arithmetic inputs also run a carry chain of n-1 bit-AND triples in `encode_walk_shares`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename RootSampler,
|
||||
typename PadRng,
|
||||
typename InputT,
|
||||
typename Beta>
|
||||
typename Beta,
|
||||
typename ...Tags>
|
||||
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)
|
||||
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec,
|
||||
Tags && ...tags)
|
||||
{
|
||||
using input_type = std::decay_t<InputT>;
|
||||
detail::ic_impl::check_input<input_type>();
|
||||
|
|
@ -570,42 +596,56 @@ auto make_dpf_doerner_shelat(arith_input_t, InputT r0, InputT r1,
|
|||
(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));
|
||||
arith_input, g0, g1, std::move(rng), detail::ic_impl::inner_lt(spec),
|
||||
std::forward<Tags>(tags)...);
|
||||
return detail::ic_impl::finish<input_type>(
|
||||
detail::ic_impl::bits_of(r), spec, std::move(inner));
|
||||
}
|
||||
|
||||
/// @brief XOR shares, sampled from the library entropy source.
|
||||
/// @return the two party keys
|
||||
/// \complexity O(n) time. One `ds_advance_level` per level: two PRG expansions and one `prepare_level`. n is `depth`.
|
||||
/// \rounds No sockets. This is the in-process transcript. A networked walk is `dpf::party::dist::point_party`.
|
||||
/// \communication none here. `local_cw_protocol` opens the correction word locally.
|
||||
/// \preprocessing Per level, `prepare_level` draws one `ds_cw_pads` (two parties × a 128-bit rand, a 128-bit gamma, and a bit) and two `ds_and_pads`. Arithmetic inputs also run a carry chain of n-1 bit-AND triples in `encode_walk_shares`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
typename Beta>
|
||||
typename Beta,
|
||||
typename ...Tags>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto make_dpf_doerner_shelat(InputT r0, InputT r1, const ic_pack<Beta> & spec)
|
||||
auto make_dpf_doerner_shelat(InputT r0, InputT r1, const ic_pack<Beta> & spec,
|
||||
Tags && ...tags)
|
||||
{
|
||||
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);
|
||||
std::move(r0), std::move(r1), rng, spec,
|
||||
std::forward<Tags>(tags)...);
|
||||
}
|
||||
|
||||
/// @brief Additive shares, sampled from the library entropy source.
|
||||
/// @return the two party keys
|
||||
/// \complexity O(n) time. One `ds_advance_level` per level: two PRG expansions and one `prepare_level`. n is `depth`.
|
||||
/// \rounds No sockets. This is the in-process transcript. A networked walk is `dpf::party::dist::point_party`.
|
||||
/// \communication none here. `local_cw_protocol` opens the correction word locally.
|
||||
/// \preprocessing Per level, `prepare_level` draws one `ds_cw_pads` (two parties × a 128-bit rand, a 128-bit gamma, and a bit) and two `ds_and_pads`. Arithmetic inputs also run a carry chain of n-1 bit-AND triples in `encode_walk_shares`.
|
||||
template <typename InteriorPRG = dpf::prg::aes128,
|
||||
typename ExteriorPRG = InteriorPRG,
|
||||
typename InputT,
|
||||
typename Beta>
|
||||
typename Beta,
|
||||
typename ...Tags>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto make_dpf_doerner_shelat(arith_input_t, InputT r0, InputT r1,
|
||||
const ic_pack<Beta> & spec)
|
||||
const ic_pack<Beta> & spec, Tags && ...tags)
|
||||
{
|
||||
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);
|
||||
arith_input, std::move(r0), std::move(r1), rng, spec,
|
||||
std::forward<Tags>(tags)...);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
|
@ -633,7 +673,7 @@ void assign_cmp(ic_key<0, Key, Input, Beta> & k0,
|
|||
const auto delta = detail::group_sub(
|
||||
detail::group_from_beta(if_true), detail::group_from_beta(if_false));
|
||||
const auto fval = detail::group_from_beta(if_false);
|
||||
assign_cmp(k0.key, k1.key,
|
||||
assign_cmp(k0.dpf_key, k1.dpf_key,
|
||||
detail::group_to_beta<Beta>(delta),
|
||||
detail::group_to_beta<Beta>(detail::group_zero(layout)));
|
||||
k0.delta_share = detail::group_mul(k0.delta_coeff, delta);
|
||||
|
|
@ -656,7 +696,7 @@ void assign_cmp(ic_key<0, Key, Input, Beta> & k0,
|
|||
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,
|
||||
assign_cmp(k0.dpf_key, k1.dpf_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);
|
||||
|
|
@ -680,6 +720,7 @@ void assign_cmp(ic_key<0, Key, Input, Beta> & k0,
|
|||
/// @param x the `x`
|
||||
/// @param memo the memoizer reused across queries
|
||||
/// @return Point evaluation
|
||||
/// \complexity Two comparison point-walks (`eval_cmp_point_impl`), each O(n) interior steps, plus O(1) group arithmetic. n is the key depth. A path memoizer reuses a shared prefix.
|
||||
template <typename IcKey, typename Query,
|
||||
typename Memo = basic_path_memoizer<typename IcKey::key_type>,
|
||||
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
|
||||
|
|
@ -707,6 +748,7 @@ auto eval_point(ic_fn, const IcKey & key, Query && x, Memo && memo = Memo{})
|
|||
/// @param to the inclusive end of the range
|
||||
/// @param buf the output buffer
|
||||
/// @param memo the memoizer reused across queries
|
||||
/// \complexity One `eval_one` per input from `from` through `to`. Each `eval_one` is two comparison point-walks, so this is not the truncated-tree interval walk. A path memoizer reuses prefixes across those walks. Counted the `for (x = a; x != b; ++x)` loop.
|
||||
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,
|
||||
|
|
@ -731,6 +773,7 @@ void eval_interval(ic_fn, const IcKey & key, Lane from, Lane to,
|
|||
}
|
||||
|
||||
/// @brief Inclusive interval `[from, to]`, with a fresh path memoizer.
|
||||
/// \complexity One `eval_one` per input from `from` through `to`. Each `eval_one` is two comparison point-walks, so this is not the truncated-tree interval walk. A path memoizer reuses prefixes across those walks. Counted the `for (x = a; x != b; ++x)` loop.
|
||||
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)
|
||||
|
|
@ -758,6 +801,7 @@ void eval_interval(ic_fn, const IcKey & key, Lane from, Lane to, Buffer && buf)
|
|||
/// @param end the iterator past the last query
|
||||
/// @param buf the output buffer
|
||||
/// @param memo the memoizer reused across queries
|
||||
/// \complexity One `eval_one` per input from `from` through `to`. Each `eval_one` is two comparison point-walks, so this is not the truncated-tree interval walk. A path memoizer reuses prefixes across those walks. Counted the `for (x = a; x != b; ++x)` loop.
|
||||
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,
|
||||
|
|
@ -769,6 +813,7 @@ void eval_sequence(ic_fn, const IcKey & key, Iter begin, Iter end,
|
|||
}
|
||||
|
||||
/// @brief Evaluate `[begin, end)`, with a fresh path memoizer.
|
||||
/// \complexity One `eval_one` per input from `from` through `to`. Each `eval_one` is two comparison point-walks, so this is not the truncated-tree interval walk. A path memoizer reuses prefixes across those walks. Counted the `for (x = a; x != b; ++x)` loop.
|
||||
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)
|
||||
|
|
@ -829,8 +874,70 @@ auto make_output_buffer(ic_fn, const IcKey & key, Lane from, Lane to)
|
|||
/// @return the opened party shares
|
||||
/// @{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
namespace ic_impl
|
||||
{
|
||||
|
||||
/// @brief Fill `out` from a verifiable IC key pair, reusing one path memoizer.
|
||||
template <typename IcKey0, typename IcKey1, typename Iter>
|
||||
void geneval_ic_eval(geneval_cmp_result & out, const IcKey0 & k0,
|
||||
const IcKey1 & k1, Iter begin, Iter end)
|
||||
{
|
||||
using key_type = unwrap_party_key_t<typename IcKey0::key_type>;
|
||||
constexpr std::size_t depth = key_type::depth;
|
||||
out.live_levels = depth;
|
||||
out.mask = k0.dpf_key.cmp().mask;
|
||||
out.cw_last = k0.dpf_key.cw_last();
|
||||
out.addend0 = k0.dpf_key.cmp_addend().raw();
|
||||
out.addend1 = k1.dpf_key.cmp_addend().raw();
|
||||
out.correction_words.resize(depth);
|
||||
out.correction_advice.resize(depth);
|
||||
if constexpr (key_type::cmp_block > 0)
|
||||
{
|
||||
out.value_cw.resize(key_type::cmp_checkpoints);
|
||||
for (std::size_t i = 0; i < key_type::cmp_checkpoints; ++i)
|
||||
out.value_cw[i] = k0.dpf_key.value_cw(i);
|
||||
out.tail_cw.resize(key_type::cmp_tail);
|
||||
for (std::size_t z = 0; z < key_type::cmp_tail; ++z)
|
||||
out.tail_cw[z] = k0.dpf_key.tail_cw(z);
|
||||
}
|
||||
else
|
||||
out.value_cw.resize(depth);
|
||||
for (std::size_t level = 0; level < depth; ++level)
|
||||
{
|
||||
out.correction_words[level] = k0.dpf_key.correction_word(level);
|
||||
out.correction_advice[level] =
|
||||
static_cast<uint8_t>(k0.dpf_key.correction_advice(level));
|
||||
if constexpr (key_type::cmp_block == 0)
|
||||
out.value_cw[level] = k0.dpf_key.value_cw(level);
|
||||
}
|
||||
// Empty query lists keep default-constructed (zero) tokens. `verify`
|
||||
// rejects the all-zero token, so an empty geneval does not verify as
|
||||
// two matching zeros.
|
||||
detail::vdpf::init_proof(out.proof0, k0.dpf_key);
|
||||
detail::vdpf::init_proof(out.proof1, k1.dpf_key);
|
||||
auto path0 = make_basic_path_memoizer(k0.dpf_key);
|
||||
auto path1 = make_basic_path_memoizer(k1.dpf_key);
|
||||
for (auto it = begin; it != end; ++it)
|
||||
{
|
||||
out.party0.push_back(opened_u64(
|
||||
eval_one(k0, *it, path0, &out.proof0), out.mask));
|
||||
out.party1.push_back(opened_u64(
|
||||
eval_one(k1, *it, path1, &out.proof1), out.mask));
|
||||
}
|
||||
detail::vdpf::fold_output_binding(out.proof0, k0.dpf_key);
|
||||
detail::vdpf::fold_output_binding(out.proof1, k1.dpf_key);
|
||||
}
|
||||
|
||||
} // namespace ic_impl
|
||||
} // namespace detail
|
||||
|
||||
/// @brief XOR mask. `r0 XOR r1` is the secret mask. Each query is
|
||||
/// returned already combined into the interval share.
|
||||
/// @details Builds a verifiable inner comparison key and folds correction
|
||||
/// seeds into `proof0` / `proof1` while reusing one path memoizer per party.
|
||||
/// An empty query range leaves both tokens zero; those do not verify.
|
||||
template <typename InputT, typename Iter, typename RootSampler, typename PadRng,
|
||||
typename Beta>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
|
|
@ -844,33 +951,8 @@ geneval_cmp_result geneval_ic(InputT r0, InputT r1, Iter begin, Iter 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));
|
||||
}
|
||||
std::move(rng), spec, dpf::verifiable{});
|
||||
detail::ic_impl::geneval_ic_eval(out, keys.first, keys.second, begin, end);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -888,38 +970,339 @@ geneval_cmp_result geneval_ic(arith_input_t, InputT r0, InputT r1, Iter begin,
|
|||
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));
|
||||
}
|
||||
std::move(rng), spec, dpf::verifiable{});
|
||||
detail::ic_impl::geneval_ic_eval(out, keys.first, keys.second, begin, end);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Grow adaptations: run on the inner `dpf_key`, then refresh `cr_share` from
|
||||
// the secret mask `r` when depth changes (`add_output` copies the fields).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template <std::size_t P0, std::size_t P1, typename Key, typename Input,
|
||||
typename Beta, typename InputT, typename... Specs>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto extend(const ic_key<P0, Key, Input, Beta> & k0,
|
||||
const ic_key<P1, Key, Input, Beta> & k1, uint64_t r, bool bit, InputT x,
|
||||
Specs &&... specs)
|
||||
{
|
||||
auto inner = dpf::extend(k0.dpf_key, k1.dpf_key, bit, x,
|
||||
std::forward<Specs>(specs)...);
|
||||
using new_raw = typename std::decay_t<decltype(inner.first)>::key_type;
|
||||
const uint64_t nmask = detail::ic_impl::input_mask_of<Input>();
|
||||
const uint64_t gmask = k0.group_mask;
|
||||
using share_t = typename ic_key<P0, new_raw, Input, Beta>::share_type;
|
||||
share_t c0{};
|
||||
share_t c1{};
|
||||
if constexpr (is_wildcard_v<Beta>)
|
||||
{
|
||||
// Wildcard: keep cr_coeff; concrete cr_share is filled by assign_cmp.
|
||||
c0 = k0.cr_share;
|
||||
c1 = k1.cr_share;
|
||||
}
|
||||
else if constexpr (detail::cmp_group_info<Beta>::custom)
|
||||
{
|
||||
using prg = typename new_raw::interior_prg;
|
||||
const auto layout = detail::group_layout<concrete_type_t<Beta>>();
|
||||
const auto cr_g = detail::group_scalar(
|
||||
detail::ic_impl::correction_s(r, k0.lo, k0.hi, nmask), layout);
|
||||
const auto target = detail::group_add(
|
||||
detail::group_mul(k0.delta_share, cr_g), // wrong: need open δ
|
||||
detail::group_zero(layout));
|
||||
(void)target;
|
||||
// Reconstruct δ = d0+d1, if_false from old cr, then new cr = δ·c_r + f.
|
||||
const auto delta = detail::group_add(k0.delta_share, k1.delta_share);
|
||||
const auto old_cr_open = detail::group_add(k0.cr_share, k1.cr_share);
|
||||
const auto old_cr_term = detail::group_mul(delta,
|
||||
detail::group_scalar(
|
||||
detail::ic_impl::correction_s(r, k0.lo, k0.hi, k0.input_mask),
|
||||
layout));
|
||||
const auto if_false = detail::group_sub(old_cr_open, old_cr_term);
|
||||
const auto new_target =
|
||||
detail::group_add(detail::group_mul(delta, cr_g), if_false);
|
||||
const auto blind = detail::group_from_node<prg>(
|
||||
dpf::uniform_sample<typename new_raw::interior_node>(), layout);
|
||||
c0 = blind;
|
||||
c1 = detail::group_sub(new_target, blind);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint64_t delta =
|
||||
(static_cast<uint64_t>(k0.delta_share)
|
||||
+ static_cast<uint64_t>(k1.delta_share))
|
||||
& gmask;
|
||||
const uint64_t old_cr_open =
|
||||
(static_cast<uint64_t>(k0.cr_share)
|
||||
+ static_cast<uint64_t>(k1.cr_share))
|
||||
& gmask;
|
||||
const uint64_t old_cr_term = detail::ic_impl::mul_mask(delta,
|
||||
detail::ic_impl::correction(r, k0.lo, k0.hi, k0.input_mask, gmask),
|
||||
gmask);
|
||||
const uint64_t if_false =
|
||||
(old_cr_open + detail::dcf_impl::neg_m(old_cr_term, gmask)) & gmask;
|
||||
const uint64_t cr =
|
||||
detail::ic_impl::correction(r, k0.lo, k0.hi, nmask, gmask);
|
||||
const uint64_t target =
|
||||
(detail::ic_impl::mul_mask(delta, cr, gmask) + if_false) & gmask;
|
||||
const uint64_t blind = detail::dcf_impl::sample_addend_blind(gmask,
|
||||
[] {
|
||||
return dpf::uniform_sample<typename new_raw::interior_node>();
|
||||
});
|
||||
uint64_t a0 = 0, a1 = 0;
|
||||
detail::incr::split_cmp_addend(target, gmask, blind, a0, a1);
|
||||
c0 = static_cast<share_t>(a0);
|
||||
c1 = static_cast<share_t>(a1);
|
||||
}
|
||||
auto out0 = detail::ic_impl::make_side<P0, new_raw, Input, Beta>(
|
||||
std::move(inner.first), k0.lo, k0.hi, nmask, gmask, k0.delta_share, c0,
|
||||
k0.delta_coeff, k0.cr_coeff);
|
||||
auto out1 = detail::ic_impl::make_side<P1, new_raw, Input, Beta>(
|
||||
std::move(inner.second), k1.lo, k1.hi, nmask, gmask, k1.delta_share, c1,
|
||||
k1.delta_coeff, k1.cr_coeff);
|
||||
out0.assigned = k0.assigned;
|
||||
out1.assigned = k1.assigned;
|
||||
return std::make_pair(std::move(out0), std::move(out1));
|
||||
}
|
||||
|
||||
template <std::size_t P0, std::size_t P1, typename Key, typename Input,
|
||||
typename Beta, typename InputT, typename... Specs>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto extend(const ic_key<P0, Key, Input, Beta> & k0,
|
||||
const ic_key<P1, Key, Input, Beta> & k1, uint64_t r, InputT x,
|
||||
Specs &&... specs)
|
||||
{
|
||||
const bool bit = detail::grow_impl::bit_at(
|
||||
static_cast<typename Key::input_type>(x), Key::depth);
|
||||
return extend(k0, k1, r, bit, x, std::forward<Specs>(specs)...);
|
||||
}
|
||||
|
||||
template <std::size_t P0, std::size_t P1, typename Key, typename Input,
|
||||
typename Beta, typename InputT, typename... Specs>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto add_output(const ic_key<P0, Key, Input, Beta> & k0,
|
||||
const ic_key<P1, Key, Input, Beta> & k1, InputT x, Specs &&... specs)
|
||||
{
|
||||
auto inner = dpf::add_output(k0.dpf_key, k1.dpf_key, x,
|
||||
std::forward<Specs>(specs)...);
|
||||
using new_raw = typename std::decay_t<decltype(inner.first)>::key_type;
|
||||
auto out0 = detail::ic_impl::make_side<P0, new_raw, Input, Beta>(
|
||||
std::move(inner.first), k0.lo, k0.hi, k0.input_mask, k0.group_mask,
|
||||
k0.delta_share, k0.cr_share, k0.delta_coeff, k0.cr_coeff);
|
||||
auto out1 = detail::ic_impl::make_side<P1, new_raw, Input, Beta>(
|
||||
std::move(inner.second), k1.lo, k1.hi, k1.input_mask, k1.group_mask,
|
||||
k1.delta_share, k1.cr_share, k1.delta_coeff, k1.cr_coeff);
|
||||
out0.assigned = k0.assigned;
|
||||
out1.assigned = k1.assigned;
|
||||
return std::make_pair(std::move(out0), std::move(out1));
|
||||
}
|
||||
|
||||
template <std::size_t P0, std::size_t P1, typename Key, typename Input,
|
||||
typename Beta, typename Memo0, typename Memo1, typename InputT,
|
||||
typename... Specs>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto extend(const ic_key<P0, Key, Input, Beta> & k0,
|
||||
const ic_key<P1, Key, Input, Beta> & k1, uint64_t r, Memo0 & m0, Memo1 & m1,
|
||||
bool bit, InputT x, Specs &&... specs)
|
||||
{
|
||||
auto inner = dpf::extend(k0.dpf_key, k1.dpf_key, m0, m1, bit, x,
|
||||
std::forward<Specs>(specs)...);
|
||||
using new_raw = typename std::decay_t<decltype(inner.first)>::key_type;
|
||||
const uint64_t nmask = detail::ic_impl::input_mask_of<Input>();
|
||||
const uint64_t gmask = k0.group_mask;
|
||||
using share_t = typename ic_key<P0, new_raw, Input, Beta>::share_type;
|
||||
share_t c0{};
|
||||
share_t c1{};
|
||||
if constexpr (is_wildcard_v<Beta>)
|
||||
{
|
||||
c0 = k0.cr_share;
|
||||
c1 = k1.cr_share;
|
||||
}
|
||||
else if constexpr (detail::cmp_group_info<Beta>::custom)
|
||||
{
|
||||
using prg = typename new_raw::interior_prg;
|
||||
const auto layout = detail::group_layout<concrete_type_t<Beta>>();
|
||||
const auto cr_g = detail::group_scalar(
|
||||
detail::ic_impl::correction_s(r, k0.lo, k0.hi, nmask), layout);
|
||||
const auto delta = detail::group_add(k0.delta_share, k1.delta_share);
|
||||
const auto old_cr_open = detail::group_add(k0.cr_share, k1.cr_share);
|
||||
const auto old_cr_term = detail::group_mul(delta,
|
||||
detail::group_scalar(
|
||||
detail::ic_impl::correction_s(r, k0.lo, k0.hi, k0.input_mask),
|
||||
layout));
|
||||
const auto if_false = detail::group_sub(old_cr_open, old_cr_term);
|
||||
const auto new_target =
|
||||
detail::group_add(detail::group_mul(delta, cr_g), if_false);
|
||||
const auto blind = detail::group_from_node<prg>(
|
||||
dpf::uniform_sample<typename new_raw::interior_node>(), layout);
|
||||
c0 = blind;
|
||||
c1 = detail::group_sub(new_target, blind);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint64_t delta =
|
||||
(static_cast<uint64_t>(k0.delta_share)
|
||||
+ static_cast<uint64_t>(k1.delta_share))
|
||||
& gmask;
|
||||
const uint64_t old_cr_open =
|
||||
(static_cast<uint64_t>(k0.cr_share)
|
||||
+ static_cast<uint64_t>(k1.cr_share))
|
||||
& gmask;
|
||||
const uint64_t old_cr_term = detail::ic_impl::mul_mask(delta,
|
||||
detail::ic_impl::correction(r, k0.lo, k0.hi, k0.input_mask, gmask),
|
||||
gmask);
|
||||
const uint64_t if_false =
|
||||
(old_cr_open + detail::dcf_impl::neg_m(old_cr_term, gmask)) & gmask;
|
||||
const uint64_t cr =
|
||||
detail::ic_impl::correction(r, k0.lo, k0.hi, nmask, gmask);
|
||||
const uint64_t target =
|
||||
(detail::ic_impl::mul_mask(delta, cr, gmask) + if_false) & gmask;
|
||||
const uint64_t blind = detail::dcf_impl::sample_addend_blind(gmask,
|
||||
[] {
|
||||
return dpf::uniform_sample<typename new_raw::interior_node>();
|
||||
});
|
||||
uint64_t a0 = 0, a1 = 0;
|
||||
detail::incr::split_cmp_addend(target, gmask, blind, a0, a1);
|
||||
c0 = static_cast<share_t>(a0);
|
||||
c1 = static_cast<share_t>(a1);
|
||||
}
|
||||
auto out0 = detail::ic_impl::make_side<P0, new_raw, Input, Beta>(
|
||||
std::move(inner.first), k0.lo, k0.hi, nmask, gmask, k0.delta_share, c0,
|
||||
k0.delta_coeff, k0.cr_coeff);
|
||||
auto out1 = detail::ic_impl::make_side<P1, new_raw, Input, Beta>(
|
||||
std::move(inner.second), k1.lo, k1.hi, nmask, gmask, k1.delta_share, c1,
|
||||
k1.delta_coeff, k1.cr_coeff);
|
||||
out0.assigned = k0.assigned;
|
||||
out1.assigned = k1.assigned;
|
||||
return std::make_pair(std::move(out0), std::move(out1));
|
||||
}
|
||||
|
||||
template <std::size_t P0, std::size_t P1, typename Key, typename Input,
|
||||
typename Beta, typename Memo0, typename Memo1, typename InputT,
|
||||
typename... Specs>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto add_output(const ic_key<P0, Key, Input, Beta> & k0,
|
||||
const ic_key<P1, Key, Input, Beta> & k1, Memo0 & m0, Memo1 & m1, InputT x,
|
||||
Specs &&... specs)
|
||||
{
|
||||
auto inner = dpf::add_output(k0.dpf_key, k1.dpf_key, m0, m1, x,
|
||||
std::forward<Specs>(specs)...);
|
||||
using new_raw = typename std::decay_t<decltype(inner.first)>::key_type;
|
||||
auto out0 = detail::ic_impl::make_side<P0, new_raw, Input, Beta>(
|
||||
std::move(inner.first), k0.lo, k0.hi, k0.input_mask, k0.group_mask,
|
||||
k0.delta_share, k0.cr_share, k0.delta_coeff, k0.cr_coeff);
|
||||
auto out1 = detail::ic_impl::make_side<P1, new_raw, Input, Beta>(
|
||||
std::move(inner.second), k1.lo, k1.hi, k1.input_mask, k1.group_mask,
|
||||
k1.delta_share, k1.cr_share, k1.delta_coeff, k1.cr_coeff);
|
||||
out0.assigned = k0.assigned;
|
||||
out1.assigned = k1.assigned;
|
||||
return std::make_pair(std::move(out0), std::move(out1));
|
||||
}
|
||||
|
||||
template <std::size_t P0, std::size_t P1, typename Key, typename Input,
|
||||
typename Beta, typename Memo0, typename Memo1, typename InputT,
|
||||
typename CwProtocol, typename... Specs>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto extend_ds(const ic_key<P0, Key, Input, Beta> & k0,
|
||||
const ic_key<P1, Key, Input, Beta> & k1, uint64_t r, Memo0 & m0, Memo1 & m1,
|
||||
InputT x0, InputT x1, CwProtocol & proto, Specs &&... specs)
|
||||
{
|
||||
auto inner = dpf::extend_ds(k0.dpf_key, k1.dpf_key, m0, m1, x0, x1, proto,
|
||||
std::forward<Specs>(specs)...);
|
||||
using new_raw = typename std::decay_t<decltype(inner.first)>::key_type;
|
||||
const uint64_t nmask = detail::ic_impl::input_mask_of<Input>();
|
||||
const uint64_t gmask = k0.group_mask;
|
||||
using share_t = typename ic_key<P0, new_raw, Input, Beta>::share_type;
|
||||
share_t c0{};
|
||||
share_t c1{};
|
||||
if constexpr (is_wildcard_v<Beta>)
|
||||
{
|
||||
c0 = k0.cr_share;
|
||||
c1 = k1.cr_share;
|
||||
}
|
||||
else if constexpr (detail::cmp_group_info<Beta>::custom)
|
||||
{
|
||||
using prg = typename new_raw::interior_prg;
|
||||
const auto layout = detail::group_layout<concrete_type_t<Beta>>();
|
||||
const auto cr_g = detail::group_scalar(
|
||||
detail::ic_impl::correction_s(r, k0.lo, k0.hi, nmask), layout);
|
||||
const auto delta = detail::group_add(k0.delta_share, k1.delta_share);
|
||||
const auto old_cr_open = detail::group_add(k0.cr_share, k1.cr_share);
|
||||
const auto old_cr_term = detail::group_mul(delta,
|
||||
detail::group_scalar(
|
||||
detail::ic_impl::correction_s(r, k0.lo, k0.hi, k0.input_mask),
|
||||
layout));
|
||||
const auto if_false = detail::group_sub(old_cr_open, old_cr_term);
|
||||
const auto new_target =
|
||||
detail::group_add(detail::group_mul(delta, cr_g), if_false);
|
||||
const auto blind = detail::group_from_node<prg>(
|
||||
dpf::uniform_sample<typename new_raw::interior_node>(), layout);
|
||||
c0 = blind;
|
||||
c1 = detail::group_sub(new_target, blind);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint64_t delta =
|
||||
(static_cast<uint64_t>(k0.delta_share)
|
||||
+ static_cast<uint64_t>(k1.delta_share))
|
||||
& gmask;
|
||||
const uint64_t old_cr_open =
|
||||
(static_cast<uint64_t>(k0.cr_share)
|
||||
+ static_cast<uint64_t>(k1.cr_share))
|
||||
& gmask;
|
||||
const uint64_t old_cr_term = detail::ic_impl::mul_mask(delta,
|
||||
detail::ic_impl::correction(r, k0.lo, k0.hi, k0.input_mask, gmask),
|
||||
gmask);
|
||||
const uint64_t if_false =
|
||||
(old_cr_open + detail::dcf_impl::neg_m(old_cr_term, gmask)) & gmask;
|
||||
const uint64_t cr =
|
||||
detail::ic_impl::correction(r, k0.lo, k0.hi, nmask, gmask);
|
||||
const uint64_t target =
|
||||
(detail::ic_impl::mul_mask(delta, cr, gmask) + if_false) & gmask;
|
||||
const uint64_t blind = detail::dcf_impl::sample_addend_blind(gmask,
|
||||
[] {
|
||||
return dpf::uniform_sample<typename new_raw::interior_node>();
|
||||
});
|
||||
uint64_t a0 = 0, a1 = 0;
|
||||
detail::incr::split_cmp_addend(target, gmask, blind, a0, a1);
|
||||
c0 = static_cast<share_t>(a0);
|
||||
c1 = static_cast<share_t>(a1);
|
||||
}
|
||||
auto out0 = detail::ic_impl::make_side<P0, new_raw, Input, Beta>(
|
||||
std::move(inner.first), k0.lo, k0.hi, nmask, gmask, k0.delta_share, c0,
|
||||
k0.delta_coeff, k0.cr_coeff);
|
||||
auto out1 = detail::ic_impl::make_side<P1, new_raw, Input, Beta>(
|
||||
std::move(inner.second), k1.lo, k1.hi, nmask, gmask, k1.delta_share, c1,
|
||||
k1.delta_coeff, k1.cr_coeff);
|
||||
out0.assigned = k0.assigned;
|
||||
out1.assigned = k1.assigned;
|
||||
return std::make_pair(std::move(out0), std::move(out1));
|
||||
}
|
||||
|
||||
template <std::size_t P0, std::size_t P1, typename Key, typename Input,
|
||||
typename Beta, typename Memo0, typename Memo1, typename InputT,
|
||||
typename CwProtocol, typename... Specs>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
auto add_output_ds(const ic_key<P0, Key, Input, Beta> & k0,
|
||||
const ic_key<P1, Key, Input, Beta> & k1, Memo0 & m0, Memo1 & m1, InputT x0,
|
||||
InputT x1, CwProtocol & proto, Specs &&... specs)
|
||||
{
|
||||
auto inner = dpf::add_output_ds(k0.dpf_key, k1.dpf_key, m0, m1, x0, x1, proto,
|
||||
std::forward<Specs>(specs)...);
|
||||
using new_raw = typename std::decay_t<decltype(inner.first)>::key_type;
|
||||
auto out0 = detail::ic_impl::make_side<P0, new_raw, Input, Beta>(
|
||||
std::move(inner.first), k0.lo, k0.hi, k0.input_mask, k0.group_mask,
|
||||
k0.delta_share, k0.cr_share, k0.delta_coeff, k0.cr_coeff);
|
||||
auto out1 = detail::ic_impl::make_side<P1, new_raw, Input, Beta>(
|
||||
std::move(inner.second), k1.lo, k1.hi, k1.input_mask, k1.group_mask,
|
||||
k1.delta_share, k1.cr_share, k1.delta_coeff, k1.cr_coeff);
|
||||
out0.assigned = k0.assigned;
|
||||
out1.assigned = k1.assigned;
|
||||
return std::make_pair(std::move(out0), std::move(out1));
|
||||
}
|
||||
|
||||
} // namespace dpf
|
||||
|
||||
#endif // LIBDPF_INCLUDE_DPF_INTERVAL_HPP__
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue