libdpf/doc/pages/yao.md

125 lines
6.2 KiB
Markdown
Raw Normal View History

# A boolean function of a leaf {#yao_leaf}
\htmlonly
<div class="eli5"><b>ELI5.</b> The key still answers the point, the comparison, and the interval. When the leaf you already hold has to go through a bit circuit the key does not contain, split that leaf into XOR bits, garble the circuit, and share the result back in the leaf's own type.</div>
\endhtmlonly
Party 0 garbles. Party 1 evaluates. Free-XOR, half-gates
([ePrint 2014/756](@ref bib_halfgates)). One table row is 32 bytes.
Outputs are XOR shares of the output bits: the garbler's share is the
permute bit, the evaluator's share is the color of the label it holds.
The circuit this library is built around is the zero-key AES-MMO block
`prg::aes128::eval` already uses on every tree expand. A seed or a leaf
can enter that block without being opened. The same netlist is a hand-built
straight line of XOR, AND, XNOR, and NOT. Inputs are declared first.
| Leaf you hold | Into the netlist | Back out |
| --- | --- | --- |
| Point leaf (subtractive) | `b2y` | `y2b` |
| Comparison leaf (additive) | `a2y` | `y2a` |
| `fss_share` | `fss2y` | `y2fss` |
| Replicated, parties 0 and 1 garble | `rss2y` | `y2rss` |
Bits are least-significant first, one byte each, 0 or 1. `width` 0 means
the whole ring on the way in, and the vector length on the way out.
Both parties' shares are arguments. The edaBit open of `x - r` and the
daBit open of `b ⊕ r` are resolved inside the call, the same way
`edabit::a2b_gmw_pair` does. Those masked values are uniform. The integer
stays shared.
`rss2y` does not wake party 2. Party 0 already holds `x0` and `x1`. Party 1
already holds `x2`. `r0.next` must equal `r1.own`. Top-level `dpf::rss2y`
and `dpf::y2rss` are the local (3,3) casts and are different functions.
```cpp
auto [k0, k1] = dpf::make_dpf(std::uint8_t{42}, std::uint32_t{0x6b});
auto s0 = *dpf::eval_point(k0, std::uint8_t{42});
auto s1 = *dpf::eval_point(k1, std::uint8_t{42});
auto [y0, y1] = dpf::yao::b2y(s0, s1, 8);
dpf::yao::netlist n;
dpf::yao::bit in[8];
for (int i = 0; i < 8; ++i)
in[i] = n.shared_in();
n.out(n.and_(in[0], in[1]));
dpf::yao::session garbler;
auto out0 = garbler.eval(0, n, y0.data(), link); // party 1 passes y1
auto [z0, z1] = dpf::yao::y2b<std::uint32_t>(out0, out1, 1);
// reconstruct(z0, z1) == (0x6b & 1) & ((0x6b >> 1) & 1)
```
`session` keeps the IKNP base OT. The first `eval` that needs a choice
label runs Chou–Orlandi. Later evals on that session only extend. Do not
interleave `iknp::sample` on the same channel. Party 0 is the garbler for
the life of the session. Tables are one-time. The model is semi-honest.
## What stays a key {#yao_not}
A public query against a secret point is a comparison key. An interval is
an interval key. A polynomial in a public offset is Grotto, one comparison
and a local dot. A product of two leaf shares is a Beaver triple. A word
mux is one bit×ring inject. `cost_pass` still chooses among a DCF mask, an
edaBit MSB, and a full adder for those.
Garble when the AND depth is the cost and the circuit is this shape: the
MMO block (5120 ANDs, one message, about 160 KiB of tables), the AES-128
block under a shared key (6400 ANDs, key schedule included), or a netlist
you built because the leaf bits are the input. The Boyar–Peralta S-box
inside those blocks is 32 ANDs. `correction_level` is that hash as one garble: eight MMO blocks, lanes
`0..3` on each seed, 40960 ANDs, XOR shares of the four-block digest.
The level and the prefix share are packed into the inputs
(`pack_correction_level`); the netlist itself does not change per level.
`party/oblivious_hash.hpp` still walks the same S-box as GMW AND layers,
80 opens per level.
## A secret branch {#yao_stack}
The netlist above is a straight line. A secret if/else, or a secret choice
among a few blocks, still belongs on that leaf circuit: the bits came from
`b2y` / `a2y` / `rss2y`, and the answer goes back with `y2b` / `y2a` /
`y2rss`. It is not a reason to open the leaf.
`yao::eval_if` stacks the two branches
([Heath and Kolesnikov, CRYPTO 2020](@ref bib_stacked)). Each branch is
garbled from the hash of the control label for that semantic bit. The
generator XORs the materials. The evaluator rebuilds the inactive branch
from a seed under the control label and XORs it out. Transmitted AND rows
follow the heavier branch, plus four translation rows per output bit so the
garbler's share does not depend on the branch.
`yao::eval_one_hot` is the same stack for `k` netlists, `k` from 2 to 8
([Heath and Kolesnikov, CCS 2021](@ref bib_onehot)). The index is
`index_p0 XOR index_p1`. The demux row for the selector color carries the
inactive seeds.
Both parties pass the same netlists. Branch inputs use the same layout as
`eval_pair`: a shared entry is that party's XOR share, `priv0` is read on
party 0, `priv1` on party 1. The reconstructed bit is `share0[i] XOR share1[i]`.
`stack_blocks` is the stacked material. `naive_blocks` is the sum of the
branches. A comparison or a public-offset polynomial still stays on the key.
## Calls {#yao_calls}
| Call | What it does |
| --- | --- |
| `yao::netlist` | XOR, AND, XNOR, NOT, XOR with a public bit. `n_and()` is the row count |
| `yao::eval_plain` | The same wires in the clear |
| `yao::eval_local` / `eval_pair` | Garble and evaluate in one process |
| `yao::session::eval` | Garble on the peer channel. Party 0 sends the tables |
| `yao::a2y` `b2y` `fss2y` `rss2y` | Ring share to LSB-first XOR bits |
| `yao::y2a` `y2b` `y2fss` `y2rss` | Those bits back to a ring share |
| `yao::aes_mmo` | One zero-key MMO block on a session. `pos` is public |
| `yao::correction_level` | Eight of those blocks: the correction-seed hash for one tree level |
| `yao::aes128` | AES-128 of a shared block under a shared key |
| `yao::eval_if` | Stacked if/else. Rows follow the heavier branch ([CRYPTO 2020](@ref bib_stacked)) |
| `yao::eval_one_hot` | One stack over `k` branches ([CCS 2021](@ref bib_onehot)) |
**Go deeper:** [a secret branch](@ref yao_stack), [yao.hpp](@ref dpf/yao.hpp),
[yao_stack.hpp](@ref dpf/yao_stack.hpp), [yao_share.hpp](@ref dpf/yao_share.hpp),
[yao_aes.hpp](@ref dpf/yao_aes.hpp), [F_Yao](@ref yao.hpp),
[F_YaoShare](@ref yao_share.hpp), [half-gates](@ref bib_halfgates),
[edaBits](@ref bib_edabits). The walk that still uses GMW for the hash is
[the dealer-free tour](@ref tour_ds).