VCVio: what this development found missing or wrong upstream
Everything below was discovered while building lean/PqStealth/ against
Verified-zkEVM/VCVio @ a5f474fd (2026-07-15, Lean v4.32.0; see
vcvio-pin.md for the pin and bump procedure). Each item says what we carry
locally because of it, where it bit, and what the upstream change would be.
Nothing here has been filed upstream yet (status as of 2026-08-19).
Grouped by what the change would cost on VCVio's side.
A. Small, mechanical — one PR each
-
DecidableEqnext tomlkem768Encoding.LatticeCrypto/MLKEM/Concrete/Instance.leansets every encoded type (EncodedTHat,EncodedU,EncodedV) toByteArray, but through a plaindef concreteEncoding, so instance search cannot see through it. Consequence:MLKEM.asKEMScheme concreteNTTRingOps mlkem768Encoding mlkem768Primitivesdoes not elaborate in a downstream file without hand-derived instances. We carry three inPqStealth/MLKEM.lean(instDecidableEqMlkem768Encoded\{THat,U,V\} := inferInstanceAs (DecidableEq ByteArray)). Upstream:@[reducible]onconcreteEncoding, or ship the three instances besidemlkem768EncodingLaws. -
De-privatize
byteEncode_size..lake/packages/VCVio/LatticeCrypto/MLKEM/Concrete/Encoding.lean:242:private theorem byteEncode_size (d : Nat) (f : Rq) : (byteEncode d f).size = 32 * d. It is exactly the fact needed for thevhalf of the FIPS 203 ciphertext layout (32·dv = 128bytes at ML-KEM-768). Because it isprivatewe take that count from the standard; it is the only non-cryptographic assumption inPqStealth/MLKEM.lean. Upstream: make it (or a public corollary) visible. -
SampleableTypeonR_q/T_q. VCVio never constructs uniform sampling on its own polynomial rings (the MLDSA files take it as a hypothesis). Any MLWE statement over the concrete ring needs it;PqStealth/SPRTwoHop.leanaddsinstSampleableTypeRq,instSampleableTypeTqand aNeZeroinstance for the ML-KEM modulus. Upstream: provide the instances inLatticeCrypto/MLKEM/Arithmetic.lean(or whereverRq/Tqlive). -
LearningWithErrors.advantage_eq_boolDistAdvantage. The bridgeadvantage = (game0).boolDistAdvantage (game1)is what lets a decision-MLWE term enter aboolDistAdvantage_trianglechain at all. We carry it asPqStealth.LearningWithErrors.advantage_eq_boolDistAdvantage(SPRTwoHop.lean); it belongs inLatticeCrypto/HardnessAssumptions/LearningWithErrors.lean.
4b. Four small boolDistAdvantage lemmas (CryptoFoundations/SecExp.lean
has only boolDistAdvantage_triangle): boolDistAdvantage_comm,
boolDistAdvantage_congr (transport along 𝒟[p] = 𝒟[p']),
boolDistAdvantage_le_sum_hybrids (telescoping over ℕ-indexed hybrids)
and boolBiasAdvantage_bind_uniformBool_branch (the bit-indexed-branch
form of boolBiasAdvantage_bind_uniformBool_eq_boolDistAdvantage). All
carried under PqStealth.ProbComp.* (Games.lean, MultiUnlink.lean).
B. Medium — real proving, but well-specified
-
A KEM-level
kem_ind_cpa_securityforMLKEM.asKEMScheme. VCVio haskpke_ind_cpa_security(about K-PKE, andsorryat the pin) and nothing about the KEM'sKEMScheme.IND_CPA_Advantage. The exact statement we need is recorded verbatim inPqStealth/MLKEM.lean("The missing upstream lemma") and elaborates as written:theorem MLKEM.kem_ind_cpa_security {params : Params} (ring : NTTRingOps) (encoding : Encoding params) (prims : Primitives params encoding) [DecidableEq encoding.EncodedTHat] [DecidableEq encoding.EncodedU] [DecidableEq encoding.EncodedV] [SampleableType SharedSecret] : ∃ mlwe : LearningWithErrors.Problem (TqMatrix params.k params.k) (TqVec params.k) (TqVec params.k), ∀ cpaAdv : (MLKEM.asKEMScheme ring encoding prims).IND_CPA_Adversary, ∃ mlweAdv : LearningWithErrors.Adversary mlwe, KEMScheme.IND_CPA_Advantage ProbCompRuntime.probComp cpaAdv ≤ |LearningWithErrors.advantage mlwe mlweAdv|The gap to
kpke_ind_cpa_securityis the T-transform half of Fujisaki–Okamoto. When it lands,mlkem768_unlinkAdvantage_le_indCpacomposes with it in a one-linecalc. -
Un-
sorryLatticeCrypto/MLKEM/Security.lean.kpke_ind_cpa_security,kpke_delta_correct,ind_cca_securityare placeholders at the pin. Downstream prose easily mistakes them for theorems (we did, and had to scrub "VCVio reduces … to MLWE" from our docs). A note worth passing along fromSPRTwoHop.lean: the two MLWE hops their K-PKE proof needs are the same two we proved, but seeded onrho— a reduction must output a real encapsulation key and cannot invertSampleNTT, so the announcedTqMatrix-sampled problem shape is unusable for the reduction adversary. The seeded shape (keyHopProblem,ctHopProblemin our tree) is the one that composes. -
KEM anonymity (ANO-CPA / ANO-CCA) in
CryptoFoundations/KeyEncapMech.lean. VCVio stops at IND-CCA. Recipient unlinkability of a stealth scheme is key privacy, not message privacy, so we define the anonymity game ourselves (PqStealth.KEM.AnonExp, the hidden bit picks the public key). Upstream should own that definition;vcvio-pin.mdsays to adopt theirs the moment it exists. References: Grubbs–Maram–Paterson (EC'22), Maram–Xagawa (PKC'23).
C. Framework-level — the ones that blocked round 3
-
RETRACTED (2026-08-19): the lazy-RO switching lemma exists. We had recorded that only
StateSeparating/IdenticalUntilBad.lean(twoQueryImpl.Statefulhandlers, explicitσ × Boolflag) was available. At the pin VCVio also hasProgramLogic/Relational/ProgrammingOracle.lean:tvDist_simulateQ_randomOracle_withProgramming_le_probEvent_bad,programming_collision_bound[_qP_qH_β], built on the generic enginetvDist_simulateQ_run_le_probEvent_output_bad(ProgramLogic/Relational/SimulateQ.lean) andQueryImpl.withProgramming/withCachingTrackingPolicy(OracleComp/QueryTracking/ProgrammingOracle.lean). What it does not cover is our spec shapeunifSpec + hashSpecwith uniform forwarding (unifFwdImpl + randomOracle, the same shape asExamples/BR93.lean, whose up-to-bad step issorryat the pin). We instantiated the engine for that shape inPqStealth/ROMUpToBad.lean(programmedROImpl,trackingROImpl, the tworelTriple_simulateQ_run'projections, and the averagedboolDistAdvantage_run'_cacheQuery_run'_empty_le); bothDKSAPOracleandBlindingROMnow close their identical-until-bad steps with it. Remaining upstream asks from this: (a) the per-step agreement lemmaprobOutput_withProgramming_eq_withCachingTrackingPolicy_of_not_bad_output'isprivate— we had to re-prove it; (b) aunifSpec + hashSpec(unifFwdImpl + so) variant of the bridge, or a general lemma that a uniform-forwarding component can be peeled off, would make ours unnecessary; (c) the symmetric half of the fundamental lemma (Pr[flag | programmed run] = Pr[flag | tracking run]) is not stated. -
Reachability of the random-oracle modules.
OracleComp/QueryTracking/RandomOracle/Basic.lean(OracleSpec.randomOracle) andOracleComp/QueryTracking/RandomOracle/DeferredSampling.lean(+ itsProbeEpsimport) are not reachable from VCVio's root import; we had to build them into the shared packages dir by hand. An import-graph fix. Also: programmability ("run against a cache that already mapsx ↦ y") is something we reconstructed ad hoc inDKSAPOracle.lean; a small API for it next torandomOraclewould help. -
Vector/Bytesuniform-projection lemma. "The first byte of a uniform 32-byte string is a uniform byte" is not provable in-tree at the pin, soPqStealth/Soundness.lean's 1-byte view-tag bound carriesviewTaguniformity as a hypothesis. A lemmaprobOutput_map_get_uniformSample_vector(or similar) inOracleComp/Constructions/SampleableType.leanwould discharge it.
D. Non-code
mathlibStandardSetlinters vs.#guard_msgs in #print axioms. Withweak.linter.mathlibStandardSet = true, thestyle.commandStartwarning produced by a#print axiomson a continuation line is captured by#guard_msgsand turns every audit block into an error. Verified on this toolchain. It discourages exactly the build-enforced axiom audit a downstream user should adopt. Worth a report; the workaround is one-line commands.
Suggested filing order
1, 2, 3, 4, 4b and the three residual asks under 8 as issues (each with the failing snippet) — clear, small asks with a concrete proposed change. 5, 6, 7 as one discussion thread — they are roadmap items for VCVio's ML-KEM security story and our seeded-hop finding is useful input to it. 9, 10, 11 as low-priority issues.