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
124
doc/pages/yao.md
Normal file
124
doc/pages/yao.md
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
# 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).
|
||||
Loading…
Add table
Add a link
Reference in a new issue