fix(phl): emit the non-negativity of the bound as a separate goal in rnd (on #1105) - #1135
Yiping106283 wants to merge 3 commits into
Conversation
22f5ad0 to
b7fa0d8
Compare
rndrnd (on #1105)
b7fa0d8 to
20a2653
Compare
| [bd] only if [bd] is non-negative. Moreover a pHL judgment is false as | ||
| soon as its bound is negative in some memory (whether or not that | ||
| memory satisfies the pre-condition): emit [0 <= bd] as a separate | ||
| (last) goal, quantified over all memories, unconditionally. *) |
There was a problem hiding this comment.
I would remove the above comment. It is at the wrong place and not clear. A comment can be added at line 242 and 268 to explain why bound check are added (a short clear one liner is ok).
| | (PNoRndParams | PSingleRndParam _), FHle -> t_side | ||
| | _ -> t_id in | ||
| FApi.t_last t_side (t_bdhoare_rnd tac_info tc) | ||
|
|
There was a problem hiding this comment.
This could be done directly inside the t_bdhoare_rnd_r function, inside the existing pattern matching. Would avoiding doing the two pattern matching here, and, if t_bdhoare_rnd is called multiple time, duplicating this code (cf. the changes in ecPhlAuto.ml and ecPhlHiAuto.ml).
bb75dba to
df3b1a7
Compare
20a2653 to
cadadf5
Compare
|
Thanks, both done. The long comment is gone and each of the two |
…`rnd` Summary: the pHL `rnd` tactic accepted `phoare[M.f : true ==> true] <= (-1)%r` for a procedure that diverges before its sampling (upstream EasyCrypt#1119), from which `false` follows with `Pr[mu_ge0]`. Root cause (src/phl/ecPhlRnd.ml, `Core.t_bdhoare_rnd_r`): for an upper bound, the rule lifts the bound check `mu d E <= bd` into the post-condition of a hoare judgment on the statements preceding the sampling. A hoare post-condition only has to hold on terminating runs, so a diverging prefix discharges it vacuously. The check is sound for the mass of the terminating runs, but the non-terminating runs contribute probability 0 to the conclusion, which is bounded by `bd` only if `0%r <= bd`: that premise was missing from the rule. Fix: the two `<=` branches that emit the hoare goal (explicit event, and event inferred from a post-condition depending on the sampled variable) now also emit `forall &hr, 0%r <= bd` as a last goal. Based on EasyCrypt#1105: under its semantics the bound must be non-negative in every memory, so the goal is unconditional (restricting it to memories satisfying `pre` would still let the rule prove a judgment whose bound is negative outside `pre`). The rule always emits it, and the rule closes the goal itself when it is trivial (`t_try t_trivial` on the last subgoal of the two `<=` arms, as `t_bdhoare_seq_r` does for its side-conditions), so trivially non-negative bounds stay effort-free; `auto` recurses only into the program-logic sub-goals of `rnd` (src/phl/ecPhlAuto.ml), so it keeps applying `rnd` when a non-trivial goal remains, and a genuinely negative bound is left as an unprovable goal. The documented rule in doc/tactics/rnd.rst is updated accordingly. Scripts that discharged the old post-condition with `rnd; skip`, `rnd; auto` or `rnd=> //` now reach the extra goal through `;`: theories/crypto/ {Birthday.eca, PROM.ec, RndExcept.eca, prp_prf/Strong_RP_RF.eca} and examples/{PRG.ec, ChaChaPoly/chacha_poly.ec, cramer-shoup/cramer_shoup.ec, global-hybrid/GlobalHybridExamp1.ec, prg-tutorial/PRGc.ec} close it with `smt` and the relevant non-negativity lemmas (since the goal no longer carries the pre-condition, facts such as `0 <= fsize m` come from the library lemmas instead). In examples/prg-tutorial/PRGc.ec the `fel` bound `(i + 1)%r * pr_dstate` is negative for `i < -1`, so under EasyCrypt#1105 the per-query judgment is false for such counter values; the bound becomes `(max 0 i + 1)%r * pr_dstate` (as EasyCrypt#1105 does in Strong_RP_RF.eca) and the sum is rewritten back with `eq_big_seq`. Test: tests/phoare-rnd-neg-bound.ec (explicit event, inferred event, and a loop-free variant). The remaining goal `forall &hr, 0%r <= -1%r` is introduced with `move=> &hr` (which fails when the goal is not emitted) and asserted unprovable with `fail (by smt())`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
cadadf5 to
f7a9ce7
Compare
26f2bfc to
c2fdd9b
Compare
Fixes #1119. Based on #1105 (
negative-phoare-false, head bb75dba); this replaces the earlier version of this PR, which was based onmain.Summary
The pHL
rndtactic acceptedphoare[M.f : true ==> true] <= (-1)%rfor a procedure that diverges before its sampling, from whichfalsefollows withPr[mu_ge0]. #1105 does not reach this: the bound check stays inside the hoare post-condition and never passes throughconseq,byprorexfalso, so with bb75dba the reproducer is still accepted and still provesfalse. The rule now emits the missing premise as a separate goal quantified over all memories, and therndtactic triest_trivialon it.Under #1105's semantics a pHL judgement is false as soon as its bound is negative in some memory, whether or not that memory satisfies the pre-condition; the side goal is therefore unconditional (
forall &hr, 0%r <= bd) because a goal restricted topre, as in #1135, would still let the rule prove a judgement whose bound is negative outsidepre.Root cause (
src/phl/ecPhlRnd.ml,Core.t_bdhoare_rnd_r)For an upper bound, the rule lifts the bound check
mu d E <= bdinto the post-condition of a hoare judgment on the statementsspreceding the sampling. A hoare post-condition only has to hold on terminating runs, so a diverging prefix discharges it vacuously. The check is sound for the mass of the terminating runs (Pr[s; x <$ d : Q] = sum_m' Pr[s](m') * mu d(m') Q <= Pr[s : true] * bd), but the non-terminating runs contribute probability 0 to the conclusion, which is bounded bybdonly if0%r <= bd: that premise was missing from the rule.Fix
The two
<=branches that emit the hoare goal (explicit event, and event inferred from a post-condition depending on the sampled variable) now also emitforall &hr, 0%r <= bdas a last goal. The rule always emits it; therndtactic (process_rnd) triest_trivialon it, so trivially non-negative bounds stay effort-free and a genuinely negative bound is left as an unprovable goal.autonow only recurses into the program-logic sub-goals produced byrnd(src/phl/ecPhlAuto.ml,t_auto_phl_rnd_r), so it keeps applyingrndwhen the extra goal is present, and its own finalt_trivialcloses the goal when it is trivial. The documented rule indoc/tactics/rnd.rstis updated accordingly (its example proof already does not check onmain:by smt(dbool1E)does not prove the1%r/2%rbound with current provers; only the goal structure is updated here).Impact
rnd; skip,rnd; autoorrnd=> //now reach the extra goal through;and must close it when it is not trivial (a symbolic bound without0%r <= bdin context). Since the goal does not carry the pre-condition, non-negativity facts that used to come from it (0 <= fsize m,0 <= qF, ...) must come from the library lemmas.smtand the relevant non-negativity lemmas on that goal, on top of Change pHL to prevent negative probabilities #1105's own script changes to the same files:theories/crypto/Birthday.eca,theories/crypto/PROM.ec,theories/crypto/RndExcept.eca,theories/crypto/prp_prf/Strong_RP_RF.eca,examples/PRG.ec,examples/ChaChaPoly/chacha_poly.ec,examples/cramer-shoup/cramer_shoup.ec,examples/global-hybrid/GlobalHybridExamp1.ec,examples/prg-tutorial/PRGc.ec. None of them proves the same side condition twice: Change pHL to prevent negative probabilities #1105 adds0%r <= bdatconseq/bypr/exfalso, this PR at therndrule.Strong_RP_RF.eca: afelbound function must be non-negative for every counter value, not only for0 <= c < q. InPRGc.ecthe bound(i + 1)%r * pr_dstateis negative fori < -1, so the per-query judgement is false under the new semantics; the bound becomes(max 0 i + 1)%r * pr_dstateand the sum is rewritten back witheq_big_seq(one line). The same pattern is what the downstream sites will need.>=and=judgements, and<=with a post-condition independent of the sampled variable (which drops the sampling), are unchanged.Downstream
The extra goal reaches the CI external projects at symbolic-bound
rndsites: cryptobox (2), sha3 (16), xmss-security (3); sphincsplus and xsalsa20 are unaffected. Those sites need script changes on top of the ones #1105 already requires (the unconditional goal also drops the pre-condition facts the #1135 versions of those changes used); diffs to follow once the base is settled, as merge requests on each project.Test
tests/phoare-rnd-neg-bound.ec(explicit event, inferred event, and a loop-free varianty <$ dnull; x <$ dunit 0): the remaining goalforall &hr, 0%r <= -1%ris introduced withmove=> &hr(which fails with "all goals are closed" on the Change pHL to prevent negative probabilities #1105 head, where the lemma goes through) and asserted unprovable withfail (by smt()).make unit96/96,make stdlib128/128,make examples49/49 (ECJOBS=3, local prover set). Reproducers: pHL seq permits negative probabilities #1100 and pHL call permits negative probabilities #1101 are rejected by Change pHL to prevent negative probabilities #1105; both pHLrnd Esoundness #1119 variants (diverging prefix, loop-free) by this commit; pHL while permits negative probabilities #1102 is out of scope here (separate PR).Note on CI: the external-project checks fail here for the same reason as on #1105 itself — CI looks for a
merge-<this branch>branch on each downstream repository and otherwise checks out their default branch, which does not have the #1105 script changes yet.