Replies: 3 comments 1 reply
|
Hi @amarshat. Unfortunately, proving the implementation of Barrett reduction is one of those things that SMT-based tools like SAW fundamentally struggle with. I wish I had a deep, technical explanation for why that is, but that gets into details of SMT solvers that are well above my pay grade. In the past, when we've proven implementations of Montgomery reduction (not quite Barrett reduction, but similar in many ways), we did so by using
When we did the aforementioned Montgomery reduction proofs, this is more or less the approach that we used. Generally, we applied one or more rewrites on the proof goal to simplify things into a form where the SMT solver could figure out what is going on. |
|
We ought to have some kind of solver or tactic that specifically lifts bitvectors to bounded integers. Transporting proofs across domains like that is in general a theoretical headache, but this particular case is really not that difficult... |
|
Thanks both, this helped. @RyanGlScott the Integer route worked for the arithmetic. I lifted the Barrett identity to unbounded (non-vacuity: swapping So the arithmetic and the model are both settled. What's left is the BV to Integer bridge itself, and that's where I'm stuck, for two concrete reasons:
it proves at width <= 56 and times out at width >= 64. So I can't hand-prove the BV to Integer rules at the width Barrett needs (product < 2^70) and @sauclovian-g this looks like exactly the "lift bitvectors to bounded integers" tactic you mentioned, and I agree the case is easy on paper. What I want is to push The two side conditions (multiply doesn't wrap, subtract doesn't borrow) both follow from Two questions:
If a Repro is the same file, |
Uh oh!
There was an error while loading. Please reload this page.
I’m trying to verify a Barrett reduction in RustCrypto’s
module_latticefield core, shared byml-dsa/ml-kem, usingmir_verify.The relevant function is:
The intended theorem is:
What I see:
I tried
z3,abc,cvc5,yices,do { goal_eval_unint []; z3; }, and a carry-folded/reformulated spec. The wider domains time out after several minutes.My current hypothesis is that the proof is going through bit-vector reasoning over:
where
xis symbolic over a2^46-wide domain. That gives a roughly 70-bit intermediate (x < 2^46timesM ≈ 2^23) and the solver ends up bit-blasting the multiply/shift shape rather than using the arithmetic structure of Barrett reduction.My question:
What is the idiomatic SAW way to discharge this kind of Barrett-style reduction proof?
Specifically, is the usual approach one of these?
Keep the proof in BV land, but abstract the quotient:
quotient = (x * M) >> k, then prove/use a side condition aboutquotient*q <= x < (quotient+1)*q + small_error, avoiding direct bit-blasting of the wide multiply/shift.Use uninterpreted functions or rewrite lemmas for the shift/multiply quotient shape, then discharge only the small Euclidean/modular obligation.
Move to unbounded integer reasoning, prove the Barrett arithmetic there, and bridge BV ↔ Integer inside SAW.
Something else entirely, such as proving/extracting a reusable lemma for the reducer and using that as an override everywhere else.
For context, we proved an analogous Montgomery reduction in a C/Cryptol pipeline by lifting the arithmetic into Isabelle. I’m trying to understand whether there is a SAW-native idiom that avoids adding a second prover for this Rust/MIR proof.
Minimal repro:
https://github.com/amarshat/pqc-assay/blob/main/implementations/rustcrypto-ml-dsa/proof/ntt/repro_barrett_2p46.saw
It is a single
mir_verifyover the reducer. It needsbuild/mldsa_harness.linked-mir.json, produced by:I can also attach the
.linked-mir.jsondirectly if that is easier.Related scaling issue:
I see a similar shape when verifying one NTT layer,
ntt_layer<128,1>, against the FIPS 204 Alg 41 spec. I currently assume the field operations withmir_unsafe_assume_spec; the overrides apply and symbolic simulation finishes, but the final proof obligation times out. The spec computes% qindependently per output coefficient, while the implementation reduces after each operation, so the final goal contains many independent modular-reduction obligations.For that case, is the preferred SAW style to make the spec compositionally use the same override terms, so the proof discharges mostly by rewriting? Or is there a standard tactic/lemma pattern for many independent modular-reduction obligations?
Layer experiment:
https://github.com/amarshat/pqc-assay/blob/main/implementations/rustcrypto-ml-dsa/proof/ntt/layer_assumed_barrett.saw
Toolchain: SAW 1.5.1, solvers bundled with that release.
Context: open formal-verification project on RustCrypto ML-DSA; scalar layer, including Barrett over
u32,ct_div, zetas, and hint logic, is already SAW-verified. This wider Barrett proof gates the NTT-layer proofs for FIPS 204 Alg 41/42.All reactions