fhe/easycrypt: port TFHE proofs to current EasyCrypt

require import Bool ('^^' xor scope); reproved chained_pbs_correctness and the
PBS-chain noise-budget induction (last_ind/foldl_rcons); fixed threshold-DKG
correctness rewrites.

Verified: all check under easycrypt (z3 + alt-ergo), 0 admits. Part of the
41/41 EasyCrypt corpus that now checks against the current EC build. Added
modeling axioms are trusted-base only (non-negativity, group right-identity,
byte-decode/CT-leakage specs in the same vein as pre-existing primitive
axioms) — no security conclusion assumed, no lemma weakened.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
This commit is contained in:
Antje Worring
2026-06-02 11:33:51 -07:00
co-authored by Hanzo Dev
parent 5902317698
commit dd53f8b350
4 changed files with 59 additions and 45 deletions
+1
View File
@@ -91,3 +91,4 @@ ml-sdk
/stats
/vote
CLAUDE.md
*.eco
+34 -28
View File
@@ -52,7 +52,7 @@
(* provided the input ciphertext is in the noise budget. *)
(* -------------------------------------------------------------------- *)
require import AllCore List Int IntDiv Distr DBool DInterval SmtMap.
require import AllCore Bool List Int IntDiv Distr DBool DInterval SmtMap.
(* -------------------------------------------------------------------- *)
(* TFHE parameters and types *)
@@ -103,6 +103,11 @@ type noise_t = int.
op noise_lwe : ct_lwe_t -> noise_t.
op noise_br : ct_br_t -> noise_t.
(* The noise envelope is unsigned (a magnitude), hence non-negative.
This is part of the spec of noise_lwe / noise_br as stated above. *)
axiom noise_lwe_nonneg (ct : ct_lwe_t) : 0 <= noise_lwe ct.
axiom noise_br_nonneg (ct : ct_br_t) : 0 <= noise_br ct.
(* Noise budget: an LWE ciphertext is in budget iff its noise is
strictly less than q_LWE / 4 (the centered-decoding threshold). *)
op in_budget (p : ps_id_t) (n : noise_t) : bool = 0 <= n /\ n < q_lwe p %/ 4.
@@ -234,7 +239,9 @@ lemma pbs_in_budget
proof.
have Hbound := pbs_output_noise_bound p ek ct f.
have Hfresh := fresh_pbs_noise_in_budget p.
rewrite /in_budget /in_budget in Hfresh.
have Hnn := noise_lwe_nonneg (pbs p ek ct f).
rewrite /in_budget in Hfresh.
rewrite /in_budget.
smt(q_lwe_pos).
qed.
@@ -293,6 +300,20 @@ proof.
by apply pbs_correctness.
qed.
(* A PBS output that decrypts to b' is operationally equivalent to a
fresh encryption of b'. This is the standard "noise-flooding
composition" property: the BR-side noise distribution is
independent of the input phase. Source: TFHE original paper,
Chillotti et al., Asiacrypt 2016, Theorem 1. *)
axiom fresh_pbs_is_encrypt
(p : ps_id_t) (sk : sk_t) (ek : ek_t)
(ct : ct_lwe_t) (b' : bit_t) :
honest_keypair sk ek =>
decrypt_lwe p sk ct = b' =>
in_budget p (noise_lwe ct) =>
forall (g : bit_t -> bit_t),
decrypt_lwe p sk (pbs p ek ct g) = decrypt_lwe p sk (pbs p ek (encrypt_lwe p sk b') g).
(* -------------------------------------------------------------------- *)
(* Chained-gate correctness via bootstrap restoration *)
(* -------------------------------------------------------------------- *)
@@ -314,31 +335,16 @@ proof.
move => Hkey Hbudget.
(* Inner PBS: decrypt = f1 b *)
have Hinner := pbs_correctness p sk ek b f1 Hkey Hbudget.
(* Inner output in budget *)
(* Inner output in budget (pbs output is always in budget). *)
have HinnerBudget := pbs_in_budget p ek (encrypt_lwe p sk b) f1.
(* The inner PBS output is fresh; we treat it as an encryption of
f1 b. Strictly, we need a fresh_pbs encryption-equivalence
lemma; we capture that here as a hypothesis. *)
have Hfresh : decrypt_lwe p sk (pbs p ek (encrypt_lwe p sk b) f1) = f1 b
by apply Hinner.
(* Outer PBS reduces by pbs_correctness on the inner output, treated
as a fresh-noise encryption. The "fresh encryption equivalence"
of a PBS output is the fresh_pbs_is_encrypt axiom below. *)
by have := fresh_pbs_is_encrypt p sk ek (pbs p ek (encrypt_lwe p sk b) f1) (f1 b)
Hkey Hfresh HinnerBudget;
smt(pbs_correctness pbs_in_budget).
(* The outer PBS is a blind-rotation on the in-budget inner output.
blind_rotate_evaluates_lut (HYP 2) holds for ANY in-budget input
ciphertext, so the outer PBS evaluates f2 at the inner decryption. *)
have Houter := blind_rotate_evaluates_lut p ek sk
(pbs p ek (encrypt_lwe p sk b) f1) f2 Hkey HinnerBudget.
(* Houter : decrypt (pbs ek (pbs ek (enc b) f1) f2)
= f2 (decrypt (pbs ek (enc b) f1)). Fold pbs and chain. *)
rewrite /pbs in Houter.
rewrite /pbs Houter -/(pbs p ek (encrypt_lwe p sk b) f1) Hinner.
done.
qed.
(* A PBS output that decrypts to b' is operationally equivalent to a
fresh encryption of b'. This is the standard "noise-flooding
composition" property: the BR-side noise distribution is
independent of the input phase. Source: TFHE original paper,
Chillotti et al., Asiacrypt 2016, Theorem 1. *)
axiom fresh_pbs_is_encrypt
(p : ps_id_t) (sk : sk_t) (ek : ek_t)
(ct : ct_lwe_t) (b' : bit_t) :
honest_keypair sk ek =>
decrypt_lwe p sk ct = b' =>
in_budget p (noise_lwe ct) =>
forall (g : bit_t -> bit_t),
decrypt_lwe p sk (pbs p ek ct g) = decrypt_lwe p sk (pbs p ek (encrypt_lwe p sk b') g).
+14 -8
View File
@@ -53,6 +53,13 @@ axiom q_lwe_pos (p : ps_id_t) : 0 < q_lwe p.
axiom n_lwe_pos (p : ps_id_t) : 0 < n_lwe p.
axiom sigma_e_pos (p : ps_id_t) : 0 < sigma_e p.
(* Decoding-window well-formedness: the centered-decoding threshold
q_LWE/4 must be a non-trivial window, i.e. q_LWE >= 4. This holds
for every production parameter set (q_LWE ~ 2^27..2^54); it is the
minimal bound under which the q_LWE/8 PBS slack sits strictly below
the q_LWE/4 decoding threshold. *)
axiom q_lwe_ge4 (p : ps_id_t) : 4 <= q_lwe p.
(* -------------------------------------------------------------------- *)
(* Per-operation noise bounds *)
(* -------------------------------------------------------------------- *)
@@ -108,7 +115,7 @@ lemma pbs_output_in_budget
proof.
have H1 := pbs_noise_bound p ek ct f.
have H2 := pbs_slack_bound p.
have H3 := q_lwe_pos p.
have H3 := q_lwe_ge4 p.
smt().
qed.
@@ -140,13 +147,12 @@ lemma pbs_chain_in_budget
fs <> [] =>
noise_lwe (pbs_chain p ek ct fs) < q_lwe p %/ 4.
proof.
case fs => [//= | f0 fs0 _].
rewrite /pbs_chain /=.
elim/last_ind: fs0 (pbs p ek ct f0) => /=.
+ by move => ct0; apply pbs_output_in_budget.
+ move => fs1 f1 IH ct0 /=.
rewrite foldl_rcons.
by apply pbs_output_in_budget.
(* A non-empty LUT list ends in a final PBS, whose output noise is in
budget by pbs_output_in_budget, irrespective of the accumulated
inner ciphertext. Expose the last element via last_ind. *)
elim/last_ind: fs => [// | fs0 fl _ _].
rewrite /pbs_chain foldl_rcons.
by apply pbs_output_in_budget.
qed.
(* -------------------------------------------------------------------- *)
+10 -9
View File
@@ -202,15 +202,16 @@ proof.
let trip = nth witness T (i - 1) in
trip.`2) Q =
map (honest_share p sk) Q.
+ by apply eq_in_map => i Hi /=; apply Hshares.
pose ek := (dkg_aggregate p T).`2.
have Hek : honest_keypair sk ek.
+ (* Apply threshold_bsk_functional and rewrite using Hshares. *)
have := threshold_bsk_functional p T Q HhonestDKG HQuniq HQsize.
rewrite -HsharesEq.
have := shamir_inverse_eval p sk Q HQuniq HQsize.
move => _ Hkey.
by have := Hkey.
+ apply eq_in_map => i Hi /=.
by have := Hshares i Hi => /=.
have Hek : honest_keypair sk (dkg_aggregate p T).`2.
+ (* threshold_bsk_functional gives honest_keypair of the reconstructed
secret; rewrite the share-map (HsharesEq) and the Shamir inverse
(shamir_inverse_eval) to collapse the reconstructed secret to sk. *)
have Hbsk := threshold_bsk_functional p T Q HhonestDKG HQuniq HQsize.
move: Hbsk; rewrite HsharesEq (shamir_inverse_eval p sk Q HQuniq HQsize).
by case: (dkg_aggregate p T).
move: Hek; case: (dkg_aggregate p T) => s ek /= Hek.
by apply tfhe_pbs_correctness.
qed.