Why every PIR scheme has to touch every record
A server that skips a row has already told you something. Working through the lower bound from first principles, and why it shapes every practical design.
The first thing anyone notices about private information retrieval is that it seems absurdly expensive. You want one record out of a million, and the protocol makes the server read all million. That is not an implementation defect waiting to be optimised away. It is forced.
The argument in one paragraph
Fix a single-server scheme and let be the set of indices the server reads while answering a query. Suppose there is some index . Then the server's entire view is independent of , so the answer it returns cannot depend on either. If the client asked for , it cannot possibly reconstruct the right value. So correctness forces to cover every index the client might have asked for — and privacy forces the distribution of to be identical for all queries. Put together: , always.
The interesting part is that this holds regardless of the cryptography. It is an information-flow argument about which memory cells were read, not about the hardness of any lattice problem.
What the server is allowed to learn
The formal privacy requirement is that for any two indices , the query distributions are computationally indistinguishable:
Note what this does not say. It says nothing about the answer size, nothing about timing, and nothing about the access pattern. Those have to be argued separately, and in practice the access pattern is the one that leaks first. A scheme that is perfectly sound under the definition above can still be broken by an adversary with a cache-timing side channel, because the definition quantifies over the transcript, not over the machine.
Where the cost actually goes
Reading records is the floor, but "reading" can mean very different things:
| Scheme family | Server work per query | Client state |
|---|---|---|
| Trivial (send everything) | transfer | none |
| LWE-based, no preprocessing | multiply-accumulate | |
| SimplePIR-style, with hint | over small integers | hint |
| Offline/online with client storage | online |
The last row looks like it violates the bound. It does not — it moves the linear pass into an offline phase that is amortised across many queries. The database still gets touched in full; you just stop paying for it on every request.
Here is the shape of the linear pass, stripped of all the cryptography:
def answer(db, query):
# db: n records, each an element of Z_p
# query: n ciphertexts, exactly one of which encrypts 1
acc = ZERO
for i in range(len(db)): # <- this loop cannot be shortened
acc = add(acc, mul(query[i], db[i]))
return accEvery practical speedup — SIMD packing, NTT batching, matrix–vector formulations — is a constant factor on that loop. None of them remove it.
Why this is good news
A lower bound that applies to every scheme is also a design constraint you can lean on. It tells you where to stop optimising, and it tells you that the honest comparison between two PIR schemes is never "how many records did you read" but rather:
- how much work per record, measured in cycles rather than asymptotics;
- how much state the client has to hold, and how it goes stale when the database updates;
- how the communication scales with the record size, not just with .
Those are the three numbers I care about in every benchmark. The linear scan is a given.