What problem are we solving?
If you’ve followed the code-agnostic IOP line — WHIR, Ligerito, the Blaze family — you’ve gotten fast, succinct, post-quantum proximity testing for “constrained interleaved linear codes.” What you have not gotten is privacy.
The proximity test reveals query answers on a codeword , and encodes the witness. Strip the rest of the protocol away and the leak is right there in the IOPP: WHIR is not zero-knowledge, and neither is anything else in this family. For most deployed SNARKs that doesn’t matter — you wrap your prover in Fiat–Shamir, you don’t care that the verifier sees the witness because there is no verifier. But the moment you build something where there is a verifier — anonymous credentials, identity, age assurance — you need a zero-knowledge proof. The honest-verifier flavor is enough; the malicious-verifier flavor follows from Fiat–Shamir for free.
The state of the art on the zero-knowledge side has been bleak:
- Concretely-efficient ZK IOPs. Exist, but pay overhead in prover and verifier time, and are tied to specific polynomial codes — Reed–Solomon, mostly.
- [BCL22] — a tensor-code-based ZK IOP that works for any zero-knowledge code, but overhead and leans on heavy PCP machinery, so not concretely efficient.
- [RW24] — pushes the overhead down to relative to witness size, but only targets constant soundness error.
None of the state-of-the-art code-agnostic IOPs provide zero-knowledge. That is the gap.
Chiesa, Fenzi, and Weissenberg (Feb 2026) close it. They present an honest-verifier zero-knowledge IOPP for constrained interleaved linear codes that:
- Works for any -additive code with a zero-knowledge encoding — fully code-agnostic.
- Costs in proof length and prover/verifier time relative to the non-private state of the art.
- Achieves round-by-round knowledge soundness with a straightline extractor and negligible error.
- Plugs into the WHIR/Ligerito recursion shape directly: Theorem 2 of the paper, together with the building blocks we’re about to develop, literally yields an HVZK variant of WHIR and Ligerito. That is the headline.
This article is Part 1 of two. We build the foundations: what a zero-knowledge encoding is, the “committed sumcheck relation” the protocol actually tests, the right definition of HVZK for interactive oracle reductions (the standard one breaks under composition — we’ll see exactly why), and the first of the two key technical pieces, an HVZK sumcheck reduction that drops a -interleaved code down to its base code without paying a overhead. Part 2 does the zero-knowledge code-switching protocol and assembles everything into the HVZK WHIR/Ligerito corollary.
Prerequisites. You should know sumcheck, multilinear extensions, finite fields, list decoding, and the shape of WHIR-style folding. We lean on the WHIR post for all of that — its folding section in particular. Out-of-domain sampling becomes load-bearing in Part 2; you can skim its WHIR-post treatment when you get there. Everything else (zero-knowledge encodings, the committed relation, HVZK for IORs) we build from scratch.
Tensoring vs. interleaving
Code-agnostic protocols need some structure to hang their tricks on, and there are two canonical ways to manufacture it from a base code: tensoring and interleaving. Sister operations, very different bills. The bill is paid in two numbers, so fix them first.
The base code and its two numbers. Pin — block length , dimension (two message symbols in, four out), minimum distance . Reed–Solomon over any with four evaluation points does exactly this. The two numbers a proximity test cares about:
- Rate . How much of the codeword is payload. Lower rate = more redundancy = longer codeword per message symbol = more prover work and a bigger oracle.
- Relative distance . How far apart codewords sit. This is what soundness is bought with: a word that isn’t a codeword must disagree with the nearest one in a -ish fraction of positions, so random spot checks catch it.
These normally trade off — Singleton says , and RS meets it. Now apply both operations and watch what happens to .
Tensoring: pay on both axes
is the set of matrices whose every row and every column is a codeword of :
How you build one. Start with a block of message symbols. Encode each of its 2 rows with → a array. Then encode each of the 4 columns with → the full . Columns are codewords by construction. Rows still are too: column-encoding applies one fixed linear map down each column, so every output row is a fixed linear combination of the two input rows, and is linear.
Why rate squares. Read the shapes off that construction: message symbols went in, symbols came out. Rate .
Why distance squares. Take any nonzero . Some column of it is nonzero — and that column is a nonzero codeword of , so it has nonzero entries. Each of those entries sits in a distinct row, so rows of are nonzero. Each such row is itself a nonzero codeword, contributing nonzero entries. Total nonzero entries out of . Relative distance , down from . Tight, too — the outer product of two minimum-weight codewords hits exactly .
The catch: those are two independent losses, not a trade. went from down to — the tensor code is strictly inside the Singleton bound now. Concretely: at block length 16 and dimension 4, a plain RS code gets . The tensor code gets at the same rate. You paid a longer codeword and handed the adversary more room, for structure.
Interleaving: pay in alphabet
The -interleaving stacks three codewords and reads them column-wise:
Length-4 word, but each “letter” is now a triple over . Nothing was combined — three codewords travel in parallel and a “position” means all three at once.
Why rate is unchanged. message symbols over in, out. Rate . Interleaving replicates the encoder; replication doesn’t change the payload fraction.
Why relative distance is unchanged. A position of is nonzero iff any of the three stacked codewords is nonzero there. If then some , and that alone already forces nonzero positions. So — and taking minimum-weight with shows it’s exactly . Block length is still 4, so , untouched.
The price is the alphabet. , and in general . One query to an interleaved codeword returns base symbols, whether or not you wanted them — so a -query test costs base-alphabet symbols. That’s what “precludes query-optimal IOPs” means: you cannot get the per-query cost below .
Side by side
| (base) | |||
|---|---|---|---|
| Block length | |||
| Alphabet | |||
| Rate | |||
| Relative distance | |||
| Buys you | — | structure on two axes | parallel claims, one position |
| Costs you | — | rate and distance | alphabet |
The two consequences that matter downstream:
- Tensoring opens up local testability and tensor sumchecks — there is structure on both axes to interrogate. This is the world TensorSwitch lives in. But the base code has to be good enough to survive squaring, and encoding is slower ( encoder calls instead of one).
- Interleaving leaves alone, so it works for every code with no parameter slack required. Encoding is independent calls — embarrassingly parallel, and streamable in small space since you never hold more than one base codeword.
The WHIR/Ligerito family lives in the interleaving world, and so does this paper. “Works for every code” is exactly the code-agnostic promise we want to keep — and the alphabet blowup, which looks like pure cost here, turns out to be a soundness lever later: the symbols a query returns share a position, and that correlation is what keeps the list-decoding union bound from exploding, in §“The HVZK sumcheck reduction”.
Zero-knowledge encodings: the foundational object
Why plain codes leak
A plain linear-code encoding is deterministic: one message in, one codeword out. Look at three coordinates of the codeword, you’ve learned three linear functions of the message. Look at enough coordinates, you can reconstruct the message exactly. That’s the entire story of why WHIR-style proximity testing leaks — the verifier’s spot checks are reading positions of a codeword that’s a fixed function of the witness, and you can’t peek at the witness “a little bit” without peeking at it.
The fix, intuitively
Make the encoding randomized. Now the same message produces many different codewords — one per choice of fresh encoder randomness. The encoder picks one at random and sends it. The promise we want: anyone reading a few coordinates of the sent codeword should learn nothing about the underlying message. Every plausible message had a way to land on those exact coordinates, so the coordinates the verifier sees are consistent with anyone’s witness.
Think of it as a one-time pad with a budget. You can hide up to coordinates by spending enough random coins on the encoding. Beyond , message structure starts showing through. The number is a parameter of the encoding — exactly the number of “free” reads the verifier is allowed without learning anything.
The smallest possible example
The mechanics fall out in the tiniest Reed–Solomon case. One message coefficient , one random coefficient . The encoder treats as a polynomial
and evaluates at fixed nonzero points . The codeword is . Now look at any single output coordinate:
Since is uniform over and is a fixed nonzero scalar, is uniform, and — the value the verifier sees — is uniform too. The message contributes a constant shift, but the random coin has already smeared everything across . One coordinate, perfectly hidden.
Two coordinates is more interesting, because links them: . The verifier can subtract them to recover , then back out . So is the budget with this small encoding.
Scaling up: hiding coordinates
Want hidden coordinates instead of one? Spend coins. Take fresh random field elements , stack them on top of the message coefficients , and encode the whole thing:
degree , evaluated at distinct nonzero . The one-coordinate example was this with .
The counting, first. has coefficients, so evaluations determine it outright — and then is yours. That budget splits cleanly: the coins absorb the first reads, and the remaining are what pin down the message coefficients. So ought to be free and ought to leak. The rest of this section makes that exact.
What we have to show is that for any query set of size , the tuple is uniform over jointly — not coordinate-by-coordinate, since two individually-uniform values can still satisfy a relation that pins down , which is how the encoding died at two queries.
Reading is affine in the coins. Fix and expand a single read, multiplying the through:
Read that as: a constant, plus a fixed row of numbers dotted with the coin vector. The row belongs to the query — it’s , computable by anyone who knows . Now write one such row per queried position and stack them. That stacking is all “collect over ” meant, and it turns the reads into a single matrix equation:
One row per query, one column per coin — so is by construction, and entry says how much coin moves read . The worked example below is literally this: , , queries at give rows and .
Where rank comes in. As ranges over all of , the product ranges over exactly the span of ‘s columns, and the -term only shifts that span bodily. So the reads fill precisely when those columns span — which is what means. Two ceilings follow immediately, and they’re the whole story:
— at most , because vectors can’t span more than dimensions; at most , because the span sits inside .
- : deficient, always. , so the image is a proper subspace of . You cannot span a -dimensional space with vectors, and no choice of code or evaluation points changes that.
- : the ceiling is , and reaches it — but only because of which numbers are in it. That is the one place the code earns its keep, and it’s what Vandermonde means.
Vandermonde, in one line. A Vandermonde matrix has geometric progressions for rows, , and with distinct a square one is invertible — a fact you already know in polynomial clothing, since a nonzero polynomial of degree cannot vanish at distinct points. Our is exactly that, row scaled by the nonzero . So for its leftmost block is an invertible Vandermonde, the rows are independent, and the rank hits .
Full rank is the property with teeth: every -tuple the verifier might read is produced by some coin vector, for every message. Uniform coins through a surjection stay uniform, and the -shift doesn’t disturb that — so the reads are uniform over whatever is, and the simulator just samples uniform field elements. Perfect -query zero-knowledge, .
Both cases on one example. Take , , so , over any with . Querying is , and is the block from above:
Invertible, so whatever target you name and whatever is, exactly one coin pair hits it — the image is all of , and the message only relabels which coins get you there. Now add , making , and take :
Both coins cancel and the message falls out in the clear. Those weights are the Lagrange coefficients for interpolating back to , and .
Such a exists whenever — a proper subspace is cut out by a linear relation, so some nonzero kills every column of and leaves , computable from what was read. The reachable tuples form a coset of ‘s column span, the message picks which coset, and reads off which one you’re on.
The cost. The codeword now carries coefficients, so you either add evaluation points to hold the rate fixed or keep and give up relative distance. Either way you pay symbols — but only has to cover the verifier’s total query budget, which grows with the security parameter and polylogarithmically in , never linearly. So , and that is one of the places the headline comes from.
The abstract object
That picture generalizes to any code. We work over -additive codes — codes whose alphabet is an -linear vector space (e.g. ). recovers ordinary linear codes; the interleaved from the previous section is -additive whenever is. The encoding is a randomized map
taking a message and randomness to a codeword. It is a -query zero-knowledge encoding with error if (i) its image lies in , and (ii) for any message and any index set of size , the distribution over fresh can be simulated up to statistical error given only — not .
Reed–Solomon with appended randomness hits the definition with (the Vandermonde argument above). [BCL22] gives linear-time-encodable instances. The construction is code-agnostic — we won’t pin down beyond this property.
Notation throughout. Bold = the message ( field elements). Plain = its codeword: . The verifier sees as an oracle; the prover knows and .
The committed sumcheck relation
What’s actually being proved
Step back. WHIR’s job — and Ligerito’s, and Blaze’s, and every other code-agnostic IOPP’s — is to convince the verifier of one shape of claim: “the message inside this codeword satisfies some specified linear constraint.” The constraint is the point of the proof; the codeword is the vehicle.
What does “a specified linear constraint” look like? Three examples worth sitting with:
- “The polynomial whose coefficients are evaluates to at point .” This is the standard polynomial-commitment opening claim. Same as — a polynomial evaluation is the inner product of coefficients with powers.
- “The first coefficient of is .” Same as .
- “The sum of ‘s coefficients is .” Same as .
All three are the same template — pick a vector, take an inner product, claim the result. Abstract it: there’s a query vector that says which question is being asked, and a claimed answer . The protocol’s job is to convince the verifier that the secret message inside the codeword satisfies .
is the question. is the answer. That’s all does. Different = different question.
The natural relation
Translate that into an IOR-shaped relation. Verifier sees the question and the claimed answer in the clear (explicit instance); has oracle access to a codeword (implicit instance); the prover knows the actual message and randomness:
Call this . Fine starting point. Leaks twice.
Leak 1: the question is too big to read. is field elements; a verifier that reads it runs in time . We want polylog. The question itself has to be encoded succinctly.
Leak 2: the answer leaks the message. Even if the encoding is perfectly zero-knowledge — so leaks nothing — sending in the clear leaks one linear projection of . That’s a real witness leak, and the protocol is going to want to talk about in the clear.
The paper resolves both with two modifications.
Modification 1 — succinct linear forms
Intuition. The first example of above — the vector of powers — has entries, but it’s described by a single field element . You don’t need to write the vector down; you can generate it from as needed. That’s the move: instead of letting live in the explicit instance as a flat -long blob, let the explicit instance be a small description of — call it a state — together with a fixed function (a “succinct linear form”) that knows how to unpack the state into the full when needed.
Formally: replace in the relation with , where is small. The verifier never reads entry-by-entry; it only ever needs to evaluate folds of (sumcheck reductions act on via this fold operation), and a fold of a succinct linear form is itself computable from in polylog time, following the WHIR recipe.
Anchor example. . State is the single field element , is the vector of powers of , and the constraint reads “the polynomial with coefficients evaluates to at .” This particular form is closely related to the sumcheck-style succinct constraints in WHIR; the two are formally incomparable but both work for our purposes.
Modification 2 — masks in the constraint
Intuition. The answer is leaky because is exactly a linear projection of the message. Stare at , you’ve stared at one bit of the witness.
The fix is the standard zero-knowledge move: add noise. Have the prover commit to a fresh random mask and sneak the mask’s contribution into the answer. The verifier now sees ; the prover knows both pieces; the verifier sees only the sum and can’t separate them. The mask absorbs the leak.
Where the mask comes from. The prover, before any of this gets sent to the verifier, samples small uniformly random vectors — one mask per “slot” we want to hide, each just a tiny block of fresh field-element noise. “Small” means is way shorter than ; these are nothing compared to the witness message .
To make these masks committable — so the verifier can ask questions about them without learning them — encode each under a separate -additive code dedicated to masks, with its own ZK encoding . The resulting codewords are sent to the verifier as additional oracles. Each mask comes with its own query vector — same role played for , just for the mask side.
The relation now demands a single joint constraint:
How this actually hides . Stare at one mask term . The mask message is uniform random over ; the query vector is fixed. The inner product of a uniform random vector with a fixed nonzero vector is itself a uniform random scalar over . So each is a fresh uniform field element, and the sum is also uniform. Read the equation:
That’s a one-time pad on . From the verifier’s view is a uniform field element — it could correspond to any value of the real projection, with the masks absorbing whatever offset is needed. Knowing leaks nothing about .
Why the verifier can’t unmask. Reasonable worry: the verifier has oracle access to the mask codewords . Could it just query them and reconstruct ? No — and this is precisely why is itself a ZK code. The same Vandermonde-style argument from §“Zero-knowledge encodings” applies: any coordinates of can be simulated given only their positions, so the mask message stays hidden behind the encoding the same way stays hidden behind . The ZK encoding on the mask side closes the loop.
Putting both modifications together gives the relation we’ll use for the rest of the article. Its data splits three ways — the standard IOR shape, made precise in §“IORs, briefly”:
| Slot | Symbol | Who sees it | What’s in it here |
|---|---|---|---|
| Explicit instance | verifier, in the clear | target , state , mask queries | |
| Implicit instance | verifier, as oracles | main codeword , mask codewords | |
| Witness | prover only | the messages and coins behind those codewords |
Data on the left, conditions on the right: is the main oracle, the are mask oracles, both honest encodings, and the last line is the joint constraint.
That’s the committed sumcheck relation, and the table shows why it needs a protocol: the constraint is about and the , which live in . The verifier has in the clear and only as oracles, so evaluating the constraint directly is exactly what it cannot do.
The “committed” in committed sumcheck relation is doing real work: the implicit instance behaves like a commitment to the message and the masks at the same time. Future protocols will accumulate more claims about this joint object; the masks travel with the protocol as oracles rather than being committed-and-opened once. Hold that thought — it’s the central design decision that makes the sumcheck step work later.
Composable HVZK for IORs (and the counterexample that forces it)
The whole construction is “compose small zero-knowledge pieces.” So we need a definition of zero-knowledge that survives composition. The textbook one doesn’t. Let’s see exactly how it breaks, because the fix is one of the conceptual contributions of the paper.
What HVZK asks for
The view is everything the verifier walks away with from one run — the prover’s messages, its own coins, its oracle query answers — as a distribution, not a fixed string.
A simulator is an efficient algorithm that manufactures a fake view from the public input alone, never the witness. The protocol is zero-knowledge if some simulator’s output lands within of the real view. The moral: anything you could derive from the transcript, you could have derived alone — so it taught you nothing.
The simulator never runs in the protocol; exhibiting one is just how the privacy claim gets discharged. You’ve already met one — §“Scaling up” closed with “sample uniform field elements.”
Honest-verifier restricts the guarantee to verifiers that sample challenges as specified. As noted up top, Fiat–Shamir makes that restriction free.
IORs, briefly
We are working with interactive oracle reductions (IORs), not IOPs. The difference: an IOR verifier doesn’t output accept/reject — it outputs a new instance for some target relation . The instance is split: an explicit part the next verifier receives as input, and an implicit part the next verifier receives as an oracle. So an IOR from to takes and produces via an interaction. Composition stitches IORs end-to-end: runs the first reduction, then runs the second on its output.
This is the right abstraction for our purposes because the WHIR/Ligerito construction is a tower of reductions: sumcheck reduces interleaving to base, code-switching reduces big code to small code, repeat, finally land in a tiny instance you can check directly.
Where the standard definition looks — and where it doesn’t
For a protocol ending in accept/reject, §“What HVZK asks for” is the whole story. An IOR doesn’t end: its verifier outputs a new instance, part of which is an oracle handed to whatever runs next.
The standard definition audits the queries this verifier made, and says nothing about the oracle passed on. That leaves somewhere to hide a leak — an oracle nobody has queried yet.
The counterexample
Let be any relation and be “check membership in directly” — same instances, empty witness slot. Two reductions:
- . Prover sends the witness as an oracle. Verifier queries nothing, then outputs . Complete and sound, and its view is empty — so a simulator that outputs nothing matches it perfectly. HVZK.
- . Prover sends nothing. Verifier reads all of and checks . Also HVZK: the standard definition hands the simulator the same oracle access the verifier has, so it reproduces that view exactly.
Each clears the bar. Now compose: ‘s output oracle is , and reads it in full. The composed verifier learns the witness.
Neither piece cheats. The leak lives in the seam: is graded on queries it never makes, and is graded on an oracle it is handed and told is fine.
The fix — put the future queries in the definition
If the danger is what happens to the output oracle later, then later has to be part of the definition. Model “whatever will query my output oracle” as an algorithm — a distinguisher, so called because it is the thing trying to tell a real view from a simulated one.
The simulator must now produce the view and answer ‘s queries into the output oracle, still without the witness:
may run as a black box — execute it on the output instance to see which positions it wants, then invent consistent answers.
Does it kill the counterexample? Take that reads the whole output oracle, exactly what does. Now ‘s simulator has to produce every position of knowing only — that is the witness, so it cannot. now fails, which is the verdict we wanted. And when queries nothing, it collapses back to the standard definition.
In practice is pinned to a class: , distinguishers making at most queries to the implicit instance.
Composition — which do you pick?
Every IOR is now HVZK only relative to some . Which one? The one that actually shows up next.
Composition theorem (Lemma 2.6 of the paper). If is HVZK with respect to with simulator , and is HVZK with respect to (running with its own distinguisher ), then is HVZK with respect to , with errors adding: .
Read slowly, because it is the whole idea: the next protocol’s simulator becomes the previous protocol’s distinguisher. In the simulated world, is the only thing that ever touches ‘s output oracle — so those are the only future queries has to survive. The statistical errors chain along the seam and add.
Each reduction we build is then proved HVZK against a bounded query class matching what the next stage will ask — the framework’s contribution being that “what happens to this oracle later” gets written down instead of assumed.
The HVZK sumcheck reduction
Now the first of the two key technical contributions: an HVZK reduction from testing the interleaving of a code to testing the base code itself. It is what kicks the verifier’s job down from a -blown-up alphabet to the original. It is also where this paper buys overhead instead of .
The setup: interleaving via multilinear decomposition
We are going to interleave by a power-of-two factor . The trick is to define the -interleaving directly in terms of a zero-knowledge encoding, by splitting the message into pieces using multilinear-polynomial structure.
Let — the familiar equality polynomial in pairs of variables.
Given a message and randomness , view them as pieces and , indexed by the boolean hypercube of side . The interleaved encoding separately encodes each piece under and stacks the results:
The multilinear interpretation: if you view and as multilinear polynomials in and variables respectively, the first variables index which “piece” you’re in, and the remaining variables index inside the piece.
Interleaved folding
The other half of the trick: how to fold an interleaved codeword. The interleaved fold of a function at a point outputs a function defined by
where is the -th coordinate of the -vector . Concretely, the verifier folds an interleaved codeword by taking the -weighted combination of its coordinates at every position.
The construction is set up so the natural identity holds:
Claim (encode-then-fold = fold-then-encode). For every message , randomness , and folding point ,
In English: folding the interleaved codeword gives you a valid base-code codeword whose message is the fold of the original messages. The prover folds messages, the verifier folds codewords, and the two stay in sync. This is a direct extension to zero-knowledge codes of the folding identities used in WHIR, STIR, and Ligerito.
The reduction, operationally
We have an instance of : a -interleaved main oracle , small mask oracles , and a joint constraint with target . The reduction outputs an instance of — same shape, but over the base code , with a new succinct linear form that encodes the fold.
Operationally:
After this single reduction, the problem size has dropped from alphabet symbols to — the size of one base codeword.
The point — small masks, not a full mask
This is where overhead is won. Prior zero-knowledge sumchecks ([CFS17], [BCL22], the binary-field variant [Dia25]) add a full random polynomial of the same size as the message, then prove the original claim relative to it via a random linear combination. That doubles prover work — every operation has to be done on a -sized object. It’s a clean construction; it’s also too expensive for our setting.
This paper does it differently: one small mask per sumcheck round, à la [XZZPS19], instead of a single full mask up front. Each mask’s message is only field elements (much smaller than , the actual message size), and its encoding a correspondingly short codeword. With rounds and , the total masking overhead is sublinear — exactly the we want.
That’s the high-level move. The non-trivial part is making it composable with the WHIR/Ligerito recursion. Three details earn their place:
- Masks travel as oracles, not opened evaluations. [XZZPS19]‘s zero-knowledge sumcheck reveals an evaluation of the mask polynomial at the end — and inside our framework that would reveal an evaluation of , breaking ZK. We cannot afford that. Instead, the reduction accumulates a claim about the masks into the next relation’s joint constraint and carries the mask oracles forward. This is exactly the work the “committed” part of was set up to do. Without the joint constraint in the relation, this sumcheck would not compose.
- . The mask ‘s contribution to round ‘s polynomial enters scaled by — the number of hypercube points it’s summed over. In characteristic 2 that factor vanishes for every round but the last, and the masks stop masking. A real instantiation constraint, but it doesn’t bite for the fields the WHIR/Ligerito family cares about (Goldilocks, BabyBear, large primes).
- Correlating queries over the larger alphabet. Round-by-round knowledge soundness wants a union bound over codewords at distance from the sent oracles. Naively, you’d “open up” the interleaved word into its base-code parts and union-bound each — leading to a union over events. That blows up the required field size to absurdity. The fix: analyze codewords relative to the interleaved codes and directly. Interleaving raises a code’s list size by only a constant factor [GGR11], so the union bound stays modest. Operationally, the verifier’s queries are correlated across the stacked base-codewords — they all share the same position — and this correlation is the soundness lever. It also shrinks compiled argument size as a bonus, because correlated queries become a single Merkle path over the bigger alphabet rather than paths.
What this buys us
After running the reduction once, we have:
- Reduced testing the -interleaving of to testing itself.
- Reduced problem size by a factor of .
- Spent extra symbols on masks — sublinear in .
- Preserved HVZK with respect to a bounded distinguisher class, by carrying mask oracles forward and accumulating their constraints.
The natural next move is to recursively shrink the codeword, which is the WHIR/Ligerito move. Each round takes a code, -interleaves it, runs this sumcheck reduction to drop the interleaving, then switches to a new, smaller code and does it again. Recurse times and the verifier ends up looking at something tiny enough to handle directly.
But the “switch to a new code” step is the second non-trivial piece, and naive code-switching is incompatible with zero-knowledge in two distinct ways. That’s Part 2.
Why we’re not done yet
We have, in zero-knowledge, the ability to reduce testing the interleaving of a code to testing the base code itself, at overhead, with composable HVZK guarantees that survive being plugged into a larger pipeline. That is half the construction.
The other half: at some point we need to stop testing and start testing something smaller — a code with shorter message length, so that recursion actually terminates in something cheap. That’s code-switching: an IOR from a test for code to a test for code . WHIR uses it, Ligerito uses it, Blaze uses it. The non-ZK version is well understood; this paper had to find the ZK version.
Two things break when you try to make the standard code-switching template zero-knowledge:
- The randomness in the encoding gets in the way. The consistency check between old and new codewords assumes the codeword is a function of the message alone. With a zero-knowledge encoding it isn’t — it’s a function of the message and the randomness — and the obvious fix (encoding both into the new code) costs another .
- Out-of-domain sampling is incompatible with privacy. WHIR’s OOD trick is what lets the protocol work in the list-decoding regime. The OOD vector forces the prover to commit to a single codeword among the possibilities — which is exactly what zero-knowledge forbids.
The resolution to both: a new gadget called a private zero-evader, and a joint OOD sample that operates on the message and fresh randomness. That, plus assembling everything into the headline corollary — an HVZK variant of WHIR/Ligerito at overhead — is Part 2.
References
- This paper — Chiesa, A., Fenzi, G., & Weissenberg, G. (Feb 2026). Zero-Knowledge IOPPs for Constrained Interleaved Codes.
- WHIR — Arnon, G., Chiesa, A., Fenzi, G., & Yogev, E. (2025). WHIR: Reed–Solomon Proximity Testing with Super-Fast Verification. EUROCRYPT ‘25. eprint
- Ligerito — Novakovic, A., & Angeris, G. (2025). Ligerito: A Small and Concretely Fast Polynomial Commitment Scheme. eprint 2025/1187
- STIR — Arnon, G., Chiesa, A., Fenzi, G., & Yogev, E. (2024). STIR: Reed–Solomon Proximity Testing with Fewer Queries. CRYPTO ‘24.
- Ligero — Ames, S., Hazay, C., Ishai, Y., & Venkitasubramaniam, M. (2017). Ligero: Lightweight Sublinear Arguments Without a Trusted Setup. CCS ‘17.
- BCL22 — Bootle, J., Chiesa, A., & Liu, S. (2022). Zero-Knowledge IOPs with Linear-Time Prover and Polylogarithmic-Time Verifier. EUROCRYPT ‘22.
- RW24 — Ron-Zewi, N., & Weiss, M. (2024). Zero-knowledge IOPs Approaching Witness Length. CRYPTO ‘24.
- XZZPS19 (Libra) — Xie, T., Zhang, J., Zhang, Y., Papamanthou, C., & Song, D. (2019). Libra: Succinct Zero-Knowledge Proofs with Optimal Prover Computation. CRYPTO ‘19.
- CFS17 — Chiesa, A., Forbes, M.A., & Spooner, N. (2017). A Zero Knowledge Sumcheck and its Applications. arXiv:1704.02086
- GGR11 — Gopalan, P., Guruswami, V., & Raghavendra, P. (2011). List Decoding Tensor Products and Interleaved Codes. SICOMP 40(5).
- Dia25 — Diamond, B. (2025). Zero-Knowledge Polynomial Commitment in Binary Fields. eprint 2025/1015
- TensorSwitch (companion blog) — TensorSwitch I.
- WHIR (companion blog) — WHIR.
- Spartan + SPARK (companion blogs) — Spartan, SPARK.