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
139
docs/STREAM_ARRAY_GAP_REPORT.md
Normal file
139
docs/STREAM_ARRAY_GAP_REPORT.md
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# Stream-array / network layer report
|
||||
|
||||
Interactive MPC/FSS that is prep plus message rounds runs on
|
||||
`net::async_stream_array` (in-process memory, unix sockets, TCP mux, parallel
|
||||
TCP, SCTP) through `async_round_sink`, `compose_async`, and the N-party runner.
|
||||
Every decision the network layer makes is an explicit, per-object setting; the
|
||||
library reads no environment variables.
|
||||
|
||||
## Configuration
|
||||
|
||||
| Setting | Type / where |
|
||||
|---------|--------------|
|
||||
| Wire policy: window, max frame, chunk size, coalescing (frames and bytes), inbox compaction, socket options | `net::wire_policy` (`net/policy.hpp`), passed to every backend constructor; `set_window_bytes` at run time |
|
||||
| Socket options: `TCP_NODELAY`, quickack, keepalive (idle/interval/count), `SO_SNDBUF`/`SO_RCVBUF`, `SCTP_NODELAY` | `net::socket_options` inside `wire_policy` |
|
||||
| Setup deadlines: join, connect, accept, handshake, drain | `net::deadlines` |
|
||||
| Lanes and framing | `drive_options::n_lanes` (`0` = 8, `lanes_one_per_round`), `framing_mode {automatic, always, never}` |
|
||||
| Wait budgets | `drive_options::wait_timeout` per round wait, `edge_timeout[edge]`, `round_timeout[round]`; the clock restarts whenever an instance advances, so a long healthy run never times out |
|
||||
| Kernels | `drive_options::workers` (compute pool) and `pump` (the sink's `io_context`, serviced while a kernel runs) |
|
||||
| Harness | `app::run_config`: transport, host, lanes, framing, instances, wire policy, pipeline credit, wait budget, compute threads, warmup, trials, CPU pins, deadlines. `from_env()` (`DPF_<KEY>`) and `apply_args()` (`--key=value`) share one parser; unknown keys and bad values throw |
|
||||
|
||||
`pipeline_credit` only lets a session submit rounds whose bytes do not depend on
|
||||
the peer, and only while that edge's window has room (`RoundSink::can_send_ahead`).
|
||||
Compose plans are fully dependent; overlap them with `instances`.
|
||||
|
||||
## Backends
|
||||
|
||||
| Backend | Notes |
|
||||
|---------|-------|
|
||||
| `async_dual_memory_hub` / `make_async_dual_memory_stream_pair` | One `io_context` per side; per-writer window; closing a side still delivers accepted writes, then EOF |
|
||||
| `async_mux_stream_array` | One TCP socket; writes are chunked and round-robined across lanes so a small write is not queued behind a large one; up to `coalesce_frames` / `coalesce_bytes` per syscall; reads land directly in a waiting reader's buffer |
|
||||
| `basic_async_parallel_stream_array` (TCP and unix) | One socket per lane with its own strand, queue, and window (`lane_window_bytes`); `accept_parallel_tcp` / `connect_parallel_tcp` use one port and a lane-index handshake |
|
||||
| `async_sctp_stream_array` (Linux + libsctp) | Lane `i` = SCTP stream `i`; per-stream chunked round-robin sends straight from the owned payload; window; `sctp_available()` checks support up front |
|
||||
| `mux_stream_array` (synchronous) | Same wire format as the async mux (interoperates with it); a poll-based pump drains reads while writing, so two large cross flushes do not deadlock |
|
||||
|
||||
All backends report `stream_stats` (wire bytes including headers, payload
|
||||
bytes, frames, write calls, buffered/unread bytes, last activity, error).
|
||||
`close()` aborts; destruction is graceful and drains accepted writes.
|
||||
|
||||
## Round sink
|
||||
|
||||
`async_round_sink(streams, slots, instances, sink_options)`:
|
||||
|
||||
* Plan-shape hello (rounds, slot widths, lanes, instances, framing, epoch); a
|
||||
mismatch fails both ends with a message naming the field.
|
||||
* Framed lanes carry `{round, nbytes}`; partial instance prefixes are ready as
|
||||
they land. Unframed lanes carry one round each.
|
||||
* `flush_round` waits while the lane's window is full, up to `drain_timeout`.
|
||||
* `sink_options::reconnect` supplies a replacement link after a transport error
|
||||
(for example `party_session::reconnector(peer)`). Both ends exchange what
|
||||
they received and resend the rest, so a plan resumes mid-run
|
||||
(`sink_stats::resumes`, `resent_bytes`).
|
||||
* Separate out and in links (`async_round_sink(out, in, ...)`) for the RSS ring.
|
||||
|
||||
## Sessions and multi-party runs
|
||||
|
||||
| Surface | Where |
|
||||
|---------|-------|
|
||||
| `party_session`: static `host:port` table (any start order), connect with retry to a deadline, per-edge transport (mux / parallel / SCTP) and wire policy, handshake that names mismatches, per-edge stats and epochs, `reconnect` / `reconnector` | `net/party_session.hpp` |
|
||||
| Dealer link in its own failure domain with its own epoch, policy, and `io_context`; `dealer_session` serves several parties | `net/party_session.hpp` |
|
||||
| `run_parties` (2 or 3 parties, dealer and RSS-ring edges) on every transport; `run_two_party` / `run_three_party` | `party_runner.hpp`, `launch.hpp` |
|
||||
| One process per party: `parse_node_args` (`--party=i --peers=host:port,...` plus any `run_config` key) and `run_node` | `party_runner.hpp`, `examples/protocol/party_node.cpp` |
|
||||
| Prep over a dealer session on the configured transport | `session::ship_prep(demand, cfg)` |
|
||||
|
||||
## Link security
|
||||
|
||||
Party links (party to party, and the dealer) run TLS 1.3 through OpenSSL on
|
||||
every socket edge unless `encryption=off`. Each party has a raw Ed25519 key
|
||||
(`identity = FILE`, made with `examples/tools/dpf_keygen.cpp`; 44 characters of
|
||||
base64, like a WireGuard key). There are no certificate files and no CA: the
|
||||
certificate TLS needs is generated in memory from the key, and peers check the
|
||||
key inside it.
|
||||
|
||||
| Configured | Effect |
|
||||
|------------|--------|
|
||||
| no keys at all | links are encrypted to a fresh key per run; nobody is authenticated; each side logs `security.no_identity` and `security.unauthenticated` |
|
||||
| `peer.N = KEY` on party M | M authenticates N; N still does not authenticate M unless it holds M's key |
|
||||
| a held key that does not match | the link fails at setup with both keys in the message |
|
||||
| `dealer_key = KEY` | the party authenticates the dealer (the dealer uses `peer.N` for parties) |
|
||||
| SCTP edge while encrypted | refused at setup (no TLS over SCTP streams); `encryption=off` allows it |
|
||||
|
||||
The session hello (party id, epoch, transport, lanes) travels inside TLS and
|
||||
carries whether the sender authenticated the receiver, so every `link.up` record
|
||||
shows `encryption=TLSv1.3/<cipher>`, `auth=key|none`, `peer_key`, and
|
||||
`peer_verified_us`.
|
||||
|
||||
Client links (`dpf/net/client_link.hpp`): a `client_listener` presents
|
||||
`server_cert`/`server_key` (PEM, CA-issued), or `server_identity`, or, with
|
||||
neither, the built-in development certificate. `connect_server` always verifies
|
||||
the server unless `client_verify=off`: a pinned key (`client_pin`), a CA chain
|
||||
for the host name (`client_ca = FILE|system`, `client_server_name`), or, when
|
||||
neither is configured, the development certificate. That certificate's private
|
||||
key is public, so the default pairing works out of the box and both ends log
|
||||
that it provides no security. A server may check client keys
|
||||
(`server_client_pin`, with `client_identity` on the client).
|
||||
|
||||
All of it is `run_config` keys, as flags, `DPF_*` variables, or a file of
|
||||
`key = value` lines (`--config=FILE`, `DPF_CONFIG`; relative paths resolve
|
||||
against the file):
|
||||
|
||||
```text
|
||||
# p0.conf
|
||||
identity = p0.key
|
||||
peer.1 = file:p1.pub
|
||||
dealer_key = 8s3b...=
|
||||
transport = mux
|
||||
```
|
||||
|
||||
## Harness
|
||||
|
||||
`app::exercise_parties(plans, inputs, kernels, ex, cfg)` runs each party's plan
|
||||
from fresh inputs for `warmup` untimed and `trials` timed repetitions and
|
||||
records the median. `experiment` writes `config.csv` (every `run_config`
|
||||
setting), `trials.csv`, and `wire.csv` (party 0's link counters) next to the
|
||||
existing tables. `experiment_bench` takes every setting as a flag.
|
||||
|
||||
## Remaining
|
||||
|
||||
1. QUIC: fits the `async_stream_array` interface; not implemented.
|
||||
2. Synchronous entry points (`tcp_pair`, `tcp_pair_mux`, `join_*_tcp_mesh`, `trio`) go through the same `peer_security` / `party_session` framework as async edges (TLS 1.3 by default). Sync mux is a blocking face over `async_stream_array`.
|
||||
3. SCTP links cannot be encrypted.
|
||||
4. The synchronous `sctp_stream_array` face still throws; use `async_sctp_stream_array`.
|
||||
5. Non-Linux SCTP is not supported; constructors throw and `sctp_available()` is false.
|
||||
|
||||
## Verification
|
||||
|
||||
`net_control_test` (27 tests) covers the controls above: config parsing, framing
|
||||
modes, hello mismatches, partial prefixes, window gating, per-edge and per-round
|
||||
budgets, a healthy run longer than its wait budget, mux round-robin and graceful
|
||||
close, per-lane parallel windows, sync/async mux interop, connect deadlines,
|
||||
static tables, transport mismatch, parallel and SCTP edges, mid-plan reconnect,
|
||||
dealer epochs, every transport through `run_parties`, a 3-party dealer and ring
|
||||
plan, separate processes from a static table, harness trials and CSVs, and prep
|
||||
over a dealer session. `share_runtime_test` (91 tests) covers the rest of the stack.
|
||||
`security_test` covers key files, TLS in every authentication combination, a
|
||||
recording relay that sees only ciphertext, wrong keys, mixed encryption
|
||||
settings, the SCTP refusal, missing-key logging, dealer keys, every client-link
|
||||
mode (development default, pins, CA chain and host name, verify off, client
|
||||
keys), config files, and two processes that authenticate each other from key
|
||||
files.
|
||||
Loading…
Add table
Add a link
Reference in a new issue