Implementation details
Summary
VIA is a lattice (RLWE/MLWE) single-server PIR family whose headline trick is to eliminate the offline/preprocessing phase while keeping online communication at \(\tilde O(\log N)\). It does this by replacing the usual "coefficient-expansion" query-expansion step with a DMux tree (logarithmic-depth homomorphic de-multiplexers) and, in the VIA-C variant, a new LWE→RLWE conversion that compresses the whole query into a handful of \(\mathbb Z_{q_1}\) elements.1
The via-rs repository is a clean,
no_std, layered Rust
port of a Python reference.2
Its primitive stack (algebra → sampling → encryption
→ switching → gates → conversion) maps faithfully
to the paper's §2–§5, and a real
Query→Answer→Recover round-trip passes at
both toy and (release-only) paper-scale parameters:
verified here, 693/693 tests green.3
VIA-C and VIA-B are implemented (VIA-B behind
the via-b cargo feature, merged after the original
audit); plain VIA is not.4
This page describes the codebase as built: the construction it implements, its crate/layer architecture, the primitive→paper map, and the build/test status. The correctness and security audit — severity-ranked findings for VIA-C and VIA-B, the paper-vs-code parameter and noise-budget divergences, and the open questions — now lives on its own page: Audit ↗. In brief it confirms the concerns external reviewers (Yue et al.) raised in the VIA Security thread, and refutes two suspected ones.
The scheme
| Variant | Offline comm. | Online comm. | Key idea | In via-rs? |
|---|---|---|---|---|
| VIA | none | \(\tilde O(1)\) | DMux tree replaces coefficient expansion; fresh control bits + ring-switch before CMux | no not implemented |
| VIA-C | allowed (public params) | \(\tilde O(1)\) | VIA + LWE→RLWE / RLWE→RGSW query compression; ring-switch inside response compression | yes fully built |
| VIA-B | allowed | batched | VIA-C + homomorphic repacking (MLWEs→RLWE) for many small records |
yes implemented
(feature via-b)
|
Sources: paper Figures 4 (VIA), 8 (VIA-C), 9 (VIA-B).1
Implementation scope confirmed by grep: the pipeline is
monomorphised against VIA-C presets and no Figure-4 / repacking
code paths exist at the audited commit
9575286 — VIA-B repacking landed later
(PR #10).4
Database layout & query decomposition
paper The database \(X\) is encoded as an \(I\times J\) grid of \(R_{n_1,p}\) polynomials, each cell packing \(d = n_1/n_2\) records.5 code The client splits an index as \(\texttt{idx} = \gamma\,(IJ) + \alpha J + \beta\) into \((\alpha,\beta,\gamma)\) = (row, column, slot); \(\alpha\) drives DMux (MSB-first), \(\beta\) drives CMux (LSB-first), \(\gamma\) drives CRot.6
Architecture
Crate & layer dependency graph
graph TD
subgraph PRIM["via-primitives (no_std)"]
L0["Layer 0 algebra: zq, rns CRT, ring NTT"]
L1["Layer 1 sampling: SHAKE-256 PRG, uniform, ternary, Gaussian"]
L2["Layer 2 encryption: SecretKey, RLWE, RLev, RGSW, MLWE, gadget, keyswitch"]
L3["Layer 3 switching: mod-switch, ring-switch, rekey"]
L4["Layer 4 gates: CMux, DMux, trees, CRot, RLWE-to-RGSW"]
L5["Layer 5 conversion: LWE-to-RLWE cascade, MLWE embed, Extr_d"]
L0 --> L1 --> L2 --> L3 --> L4 --> L5
L2 --> L4
end
subgraph PROTO["via-protocol"]
P1["PIRParams and presets TOY / REALISTIC"]
P2["wire: Query, Answer, keys (serialization partial)"]
end
CL["via-client: setup, query idx, recover"]
SV["via-server: setup_db, query_decomp, first_dim, answer, resp_comp"]
INT["via-integration: e2e tests, KATs, benches"]
PRIM --> PROTO
PROTO --> CL
PROTO --> SV
PRIM --> CL
PRIM --> SV
CL --> INT
SV --> INT
VIA-C end-to-end sequence
sequenceDiagram participant C as Client participant S as Server DB I x J Note over C: setup yields keys s, S1, S2 and public params pp_qck, pp_rck C->>C: split idx into alpha row, beta col, gamma slot, then encrypt bits as LWE at q1 C->>S: query = L log N LWE ciphertexts Note over S: Answer pipeline, Figure 8 S->>S: 1. QueryDecomp LWE to RLWE to RGSW yields C_ctrl, C_sel, C_rot S->>S: 2. DMux tree on C_ctrl yields one-hot rows at q1 S->>S: 3. ModSwitch q1 to q2 S->>S: 4. First dimension sum over i of c_i times db at i,j at q2 S->>S: 5. CMux tree on C_sel selects column S->>S: 6. CRot on C_rot rotates to slot gamma S->>S: 7. RespComp ModSwitch q2 to q3, RingSwitch n1 to n2, scale to q4 S->>C: answer = mask at q3, body at q4 C->>C: Recover m = round of p times B' minus round of q4 A' S2 / q3, all over q4, mod p
Answer noise/modulus journey
flowchart TD A["LWE at q1 (compressed query bits)"] B["RGSW gate keys at q1: C_ctrl, C_sel, C_rot"] C["one-hot rows at q1 (noise + theta_DMux times log I)"] D["rows at q2"] E["column sums at q2 (noise times I n1 p^2 / 4, dominant term)"] F["selected column at q2 (noise + theta_CMux times log J)"] G["rotated to slot at q2"] H["answer, small ring n2 at q4 (decodes iff theta_ans under Delta/2 = 128)"] A -->|"LWE-to-RLWE-to-RGSW decompress"| B B -->|"DMux tree x log I"| C C -->|"ModSwitch q1 to q2"| D D -->|"FirstDim sum of c times db, x I"| E E -->|"CMux tree x log J"| F F -->|"CRot"| G G -->|"RespComp: q2 to q3, RingSwitch n1 to n2, q3 to q4"| H style E fill:#fdecea,stroke:#c0392b style H fill:#eafaf1,stroke:#1f9d6b
Modulus chain \(q_1{>}q_2{>}q_3{>}q_4{>}p\); step labels follow Appendix C's variance recursion.8 The first-dimension \(\times I\,n_1\,p^2/4\) term (red) and the per-level \(\log I,\log J\) additions are where the budget is spent; the answer (green) decodes only if the accumulated variance stays under \(\Delta/2\).
Primitive → paper map
| Primitive | Where (code) | Paper § | Notes |
|---|---|---|---|
| Ring \(R_{n,q}\), NTT, RNS \(q_1\) | algebra/{zq,rns,ring} |
§2, §0 | 2-prime RNS for \(q_1\approx2^{75}\); const & runtime modulus impls |
| RLWE / RLev / RGSW / MLWE |
encryption/{rlwe,rlev,rgsw,mlwe}.rs
|
§2.1–§2.3 | RLev = \(\ell\) RLWE at gadget powers; RGSW = pair of RLev |
| Gadget decomposition (approximate) | encryption/gadget.rs |
§2.3 / [40] | signed-balanced digits; reconstruction error absorbed into noise7 |
| CMux / DMux + trees | gates/mux.rs |
§3.1, Lemma C.1/C.2 |
external-product based; cmux_tree /
dmux_tree allocation-free
|
| CRot (controlled rotation) | gates/rotate.rs |
§4.2 | RGSW-controlled \(X^{-\gamma}\) rotation |
| RLWE→RGSW conversion | gates/convert.rs |
§4.2 / [45] | gadget product with RLev of \(S^2\) |
| LWE→RLWE conversion cascade |
conversion/{conv,cascade,extract}.rs
|
§4.1, Lemma 4.1/4.2 | iterated MLWE-to-MLWE; \(O(n\log n)\) noise |
| Mod-switch / ring-switch / rekey | switching/* |
§2.4, §3.3 | ring-switch is a homomorphic projection \(n_1\!\to\!n_2\) |
| Sampling (PRG, ternary, Gaussian) | sampling/* |
§1.1–§1.6 | SHAKE-256 counter PRG; Box-Muller Gaussian (only float path) |
Build & test status
code
Built and run on Rust stable 1.96.0.
cargo test --workspace:
693 passed, 0 failed, 4 ignored.9
-
code
Toy-parameter
Query→Answer→Recoverround-trips pass and assertrecovered == record[i]across all indices (e2e_toy.rs,client_server_e2e.rs).3 -
code
Cross-language byte-parity KATs
(
layer3/4/5/6_kats) pin primitive outputs against Python-generated constants — all pass.10 -
code
The ignored tests are the heavy paper-scale (n2048 RNS)
cases;
cargo test --release -p via-integration --test client_server_e2e_paper -- --ignored→ 2 passed in 35.7s (client_server_e2e_paper_scale_index_15recovers the right record at n1=2048/n2=512, q1≈2^75).11 -
code
A
cargo-fuzzharness covers 9 areas (zq, rns, sampling, switching, encryption, gates, conversion, ring, protocol).12 - inference The green paper-scale round-trip confirms the pipeline computes correctly at paper dimensions, but it runs ternary keys on a 2×2 DB, so it does not witness the \(2^{-40}\) noise-budget claim at paper σ/dimensions (A3).
-
inference The crate
graph above is the layout at the audited commit
9575286(five workspace crates). A sixth crate,via-estimator(a calibrated core-SVP lattice estimate plus the Appendix-C noise/\(P_\text{fail}\) calculator), was added afterwards and is now the in-repo source of trust for the security-bit and \(2^{-40}\) correctness numbers used on the Parameters and Audit pages (cargo run -p via-estimator).13
Footnotes
- Liu et al. (2025) (local: via-paper.pdf) §1 Introduction & §3.1, p.1,8 — "VIA substitutes coefficient expansion techniques with DMux … introducing only logarithmic noise variance"; Figures 4/8/9 give the three constructions. ↩ ↩
-
protocol/presets.rs
and many provenance comments (e.g.
client.py:117,server.py:196,server/resp_comp.rs) reference a Python reference;find … *.pyover the tree returns only a CI script — the Python source is not in this checkout. ↩ -
server/e2e_toy.rs
and
integration/client_server_e2e.rs
— full-index
assert_eq!(recover(query(i)), record[i])at toy params. ↩ ↩ -
grep over crates/{client,server,protocol}: pipeline
monomorphised against
ViaCPublicParams; no Figure-4 (ring-switch-before-CMux) path, norepack/via_b/MLWEs→RLWE. "VIA-B (Layer 7) will wrap it" — server/answer.rs. Accurate as of commit9575286; VIA-B repacking (repack/via_b) was subsequently merged in PR #10 and postdates this audit. ↩ ↩ - Liu et al. (2025) (local: via-paper.pdf) §3.1 (Database structure), p.8 — \(db[i,j]=\iota_{n_2\to n_1}((X_{kIJ+iJ+j})_k)\), grid \(I\times J\). ↩
-
client/decompose.rs
—
index = γ·(I·J)+α·J+β; DMux bits MSB-first, CMux/CRot bits LSB-first. ↩ - primitives/gadget.rs — "signed-balanced digits in \((-B/2,B/2]\) … the '≈' hides a reconstruction error bounded by \(\mathrm{round}(q/B^L)/2\)." ↩
- Liu et al. (2025) (local: via-paper.pdf) Appendix C.2, p.25-27 — the per-step variance recursion (θctrl→θdmux→θms→θfirst→θcmux→θcrot→θrep→θans) and condition \(\lVert E_{\text{final}}\rVert_\infty\le\lfloor(q_3-q_4)/p\rceil/2-1\). ↩
-
Executed in this environment after installing rustup/stable
(1.96.0):
cargo test --workspace→ 693 passed / 0 failed / 4 ignored across the workspace crates' unit, integration, and doctest targets. ↩ - crates/via-primitives/tests/{layer3,layer4,layer5}_kats.rs and crates/via-integration/tests/layer6_kats.rs — byte-equality vs embedded Python-generated constants. ↩
-
integration/client_server_e2e_paper.rs
(NUM_ROWS=2,NUM_COLS=2), :69-71 (Distribution::Ternary),
:146 (
#[ignore]). Both ignored tests pass in release (verified, 35.7s). ↩ - via-rs/fuzz/fuzz_targets/{zq,rns,sampling,switching,encryption,gates,conversion,ring,protocol} and fuzz/Cargo.toml target list. ↩
-
estimator/main.rs
— the
via-estimatorcrate postdates the audited commit9575286(so it is absent from the crate graph above, which is pinned to that commit); per the workspace source-of-trust rule it is the authoritative in-repo derivation for every security-bit and \(2^{-40}\)-correctness figure. ↩