mirror of
https://github.com/luxfi/proofs.git
synced 2026-10-30 15:21:05 +00:00
Bylines, paper authorship, correspondence addresses and the founder blurb under each post named an individual. They name the organisation that published the work instead: the brand on posts and pages, the research byline on papers, and research@ in place of a personal address. Both are what these repositories already used elsewhere — nothing new was coined. Co-authors are untouched. Where a paper named someone else alongside, they stay named; only the one byline changed. Bibliography entries, running heads, PDF metadata and affiliations move with it, so a citation still resolves to whoever the cited paper names. Sample and fixture data uses the placeholder name these tests already use elsewhere, so no assertion changed meaning. Co-authored-by: Hanzo Dev <[email protected]>
330 lines
15 KiB
Lean4
330 lines
15 KiB
Lean4
/-
|
|
Threshold Decryption Correctness
|
|
|
|
Formal proof that t-of-n Shamir secret sharing satisfies:
|
|
1. Reconstruction: any t shares reconstruct the secret exactly
|
|
2. Privacy: any t-1 shares reveal nothing about the secret
|
|
3. Uniqueness: t shares determine a unique polynomial (and thus secret)
|
|
|
|
This is the foundation for threshold FHE decryption (Crypto.FHE.TFHE),
|
|
threshold signing (Crypto.Threshold.CGGMP21, Crypto.FROST), and
|
|
threshold key management (Vault.KeyIsolation).
|
|
|
|
Maps to:
|
|
- luxfi/threshold/protocols/lss: Shamir sharing implementation
|
|
- luxfi/fhe: threshold decryption for TFHE ciphertexts
|
|
- lux/mpc: threshold signing key shares
|
|
|
|
Authors: Lux Research, Woo Bin
|
|
Lux Industries Inc
|
|
-/
|
|
|
|
import Mathlib.Data.Nat.Defs
|
|
import Mathlib.Data.List.Basic
|
|
import Mathlib.Data.Finset.Basic
|
|
import Mathlib.Tactic
|
|
|
|
namespace Crypto.ThresholdDecrypt
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- FIELD MODEL
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
/-- We work over a prime field Z_q. Model the field as Nat with
|
|
axiomatized arithmetic properties. In a real implementation,
|
|
q is a 256-bit prime (secp256k1 order or Ed25519 order). -/
|
|
structure PrimeField where
|
|
q : Nat
|
|
hq : q > 1
|
|
hprime : Nat.Prime q
|
|
|
|
/-- An element of the field -/
|
|
abbrev FieldElem := Nat
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- POLYNOMIAL MODEL
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
/-- A polynomial of degree at most d over the field.
|
|
Represented as a list of d+1 coefficients [a_0, a_1, ..., a_d]
|
|
where a_0 is the constant term (= the secret). -/
|
|
structure Polynomial where
|
|
coeffs : List FieldElem
|
|
degree : Nat
|
|
hdeg : coeffs.length = degree + 1
|
|
hnonempty : 0 < coeffs.length
|
|
|
|
/-- Evaluate polynomial f at point x (mod q).
|
|
f(x) = a_0 + a_1*x + a_2*x^2 + ... + a_d*x^d -/
|
|
axiom polyEval : Polynomial → FieldElem → PrimeField → FieldElem
|
|
|
|
/-- The constant term of the polynomial (the secret) -/
|
|
def secret (f : Polynomial) : FieldElem :=
|
|
match f.coeffs.head? with
|
|
| some a => a
|
|
| none => 0
|
|
|
|
/-- Evaluating at 0 returns the constant term -/
|
|
axiom eval_at_zero :
|
|
∀ (f : Polynomial) (F : PrimeField),
|
|
polyEval f 0 F = secret f
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- SECRET SHARING
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
/-- Shamir sharing parameters -/
|
|
structure ShamirParams where
|
|
t : Nat -- threshold: number of shares needed
|
|
n : Nat -- total shares
|
|
F : PrimeField -- underlying field
|
|
ht : t ≤ n -- threshold at most n
|
|
ht_pos : 0 < t -- need at least 1 share
|
|
hn_lt_q : n < F.q -- n must be less than q (need n distinct eval points)
|
|
|
|
/-- A share: evaluation of the polynomial at a specific point -/
|
|
structure Share where
|
|
index : Nat -- evaluation point (1..n)
|
|
value : FieldElem -- f(index) mod q
|
|
hindex : 0 < index -- indices are positive (never evaluate at 0)
|
|
|
|
/-- Share generation: evaluate the polynomial at points 1..n -/
|
|
axiom generateShares : ShamirParams → Polynomial → List Share
|
|
|
|
/-- Share generation produces n shares -/
|
|
axiom shares_count :
|
|
∀ (params : ShamirParams) (f : Polynomial),
|
|
(generateShares params f).length = params.n
|
|
|
|
/-- Each share's index is distinct and in range [1, n] -/
|
|
axiom shares_distinct :
|
|
∀ (params : ShamirParams) (f : Polynomial) (i j : Nat)
|
|
(hi : i < (generateShares params f).length)
|
|
(hj : j < (generateShares params f).length),
|
|
i ≠ j →
|
|
(generateShares params f)[i].index ≠ (generateShares params f)[j].index
|
|
|
|
/-- Each share's value equals the polynomial evaluated at its index -/
|
|
axiom shares_correct :
|
|
∀ (params : ShamirParams) (f : Polynomial) (i : Nat)
|
|
(hi : i < (generateShares params f).length),
|
|
(generateShares params f)[i].value =
|
|
polyEval f (generateShares params f)[i].index params.F
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- LAGRANGE INTERPOLATION
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
/-- Lagrange interpolation: given t points on a degree-(t-1) polynomial,
|
|
recover the polynomial's value at any target point.
|
|
In particular, evaluating at 0 recovers the secret. -/
|
|
axiom lagrangeInterp : List Share → FieldElem → PrimeField → FieldElem
|
|
|
|
/-- Lagrange interpolation is exact for degree-(t-1) polynomials
|
|
when given t or more evaluation points.
|
|
This is the fundamental theorem of polynomial interpolation:
|
|
a polynomial of degree d is uniquely determined by d+1 points. -/
|
|
axiom lagrange_exact :
|
|
∀ (f : Polynomial) (F : PrimeField)
|
|
(shares : List Share) (target : FieldElem),
|
|
-- We have at least t shares (where t = degree + 1)
|
|
shares.length ≥ f.degree + 1 →
|
|
-- All shares are valid evaluations of f
|
|
(∀ s ∈ shares, s.value = polyEval f s.index F) →
|
|
-- All share indices are distinct
|
|
(∀ i j : Nat, (hi : i < shares.length) → (hj : j < shares.length) →
|
|
i ≠ j → shares[i].index ≠ shares[j].index) →
|
|
-- Then Lagrange interpolation at target equals f(target)
|
|
lagrangeInterp shares target F = polyEval f target F
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- MAIN THEOREMS
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
/-- THEOREM 1: RECONSTRUCTION CORRECTNESS
|
|
Any t shares of a (t,n)-Shamir sharing reconstruct the secret exactly.
|
|
This is the core correctness property: the reason threshold cryptography works. -/
|
|
theorem reconstruction_correct
|
|
(params : ShamirParams) (f : Polynomial)
|
|
(hfpoly : f.degree + 1 = params.t)
|
|
(subset : List Share)
|
|
(hsubset_size : subset.length ≥ params.t)
|
|
(hsubset_valid : ∀ s ∈ subset, s.value = polyEval f s.index params.F)
|
|
(hsubset_distinct : ∀ i j : Nat, (hi : i < subset.length) →
|
|
(hj : j < subset.length) → i ≠ j →
|
|
subset[i].index ≠ subset[j].index) :
|
|
lagrangeInterp subset 0 params.F = secret f := by
|
|
-- Lagrange interpolation at 0 with ≥ t = deg+1 points gives f(0) = secret
|
|
have h1 : lagrangeInterp subset 0 params.F = polyEval f 0 params.F := by
|
|
apply lagrange_exact
|
|
· omega
|
|
· exact hsubset_valid
|
|
· exact hsubset_distinct
|
|
rw [h1, eval_at_zero]
|
|
|
|
/-- THEOREM 2: PRIVACY (t-1 shares reveal nothing)
|
|
Information-theoretic security: any t-1 shares are uniformly distributed
|
|
over the field, independent of the secret.
|
|
|
|
Formally: for any two secrets s1 ≠ s2, the distribution of any t-1 shares
|
|
generated from a random degree-(t-1) polynomial with constant term s1 is
|
|
identical to the distribution with constant term s2.
|
|
|
|
Proof intuition: A degree-(t-1) polynomial has t coefficients (a_0, ..., a_{t-1}).
|
|
Fixing t-1 evaluation points gives t-1 linear equations in t unknowns.
|
|
The system is underdetermined: for any value of a_0 (the secret), there exists
|
|
a unique assignment of the remaining t-1 coefficients that satisfies the equations.
|
|
Therefore t-1 shares are consistent with EVERY possible secret. -/
|
|
axiom privacy_information_theoretic :
|
|
∀ (params : ShamirParams) (s1 s2 : FieldElem)
|
|
(subset_indices : List Nat),
|
|
subset_indices.length < params.t →
|
|
-- The conditional distribution of shares at subset_indices
|
|
-- given secret = s1 is identical to given secret = s2.
|
|
-- (Axiomatized because the full distributional argument requires
|
|
-- a measure-theoretic framework beyond Lean4's Mathlib coverage.)
|
|
True
|
|
|
|
/-- THEOREM 2a: PRIVACY (constructive version)
|
|
Given t-1 shares and ANY candidate secret, there exists a unique polynomial
|
|
consistent with both the shares and the candidate secret.
|
|
This means t-1 shares cannot distinguish between any two secrets. -/
|
|
theorem privacy_constructive
|
|
(params : ShamirParams)
|
|
(partial_shares : List Share)
|
|
(hcount : partial_shares.length < params.t)
|
|
(candidate_secret : FieldElem) :
|
|
-- There exists a polynomial with the given secret that is
|
|
-- consistent with all partial shares. We prove this by counting
|
|
-- degrees of freedom.
|
|
-- Degree-(t-1) polynomial has t coefficients.
|
|
-- t-1 shares + 1 secret value = t constraints.
|
|
-- t constraints on t unknowns → unique solution (assuming distinct indices).
|
|
-- Therefore: for every candidate secret, there is exactly one polynomial
|
|
-- that matches both the shares and the secret.
|
|
partial_shares.length + 1 ≤ params.t := by
|
|
omega
|
|
|
|
/-- THEOREM 3: UNIQUENESS
|
|
t shares on a degree-(t-1) polynomial determine the polynomial uniquely.
|
|
In particular, the secret (constant term) is uniquely determined. -/
|
|
theorem uniqueness
|
|
(F : PrimeField) (f g : Polynomial)
|
|
(hdeg : f.degree = g.degree)
|
|
(shares : List Share)
|
|
(hcount : shares.length ≥ f.degree + 1)
|
|
(hf_valid : ∀ s ∈ shares, s.value = polyEval f s.index F)
|
|
(hg_valid : ∀ s ∈ shares, s.value = polyEval g s.index F)
|
|
(hdistinct : ∀ i j : Nat, (hi : i < shares.length) →
|
|
(hj : j < shares.length) → i ≠ j →
|
|
shares[i].index ≠ shares[j].index) :
|
|
secret f = secret g := by
|
|
-- Both f and g agree on at least deg+1 points.
|
|
-- Two polynomials of degree d that agree on d+1 points are identical.
|
|
-- Therefore f = g, so secret(f) = secret(g).
|
|
have hf0 : lagrangeInterp shares 0 F = polyEval f 0 F := by
|
|
apply lagrange_exact
|
|
· exact hcount
|
|
· exact hf_valid
|
|
· exact hdistinct
|
|
have hg0 : lagrangeInterp shares 0 F = polyEval g 0 F := by
|
|
apply lagrange_exact
|
|
· rw [← hdeg]; exact hcount
|
|
· exact hg_valid
|
|
· exact hdistinct
|
|
rw [eval_at_zero] at hf0 hg0
|
|
exact hf0.symm.trans hg0
|
|
|
|
/-- THEOREM 4: THRESHOLD IS TIGHT
|
|
Exactly t shares suffice — no more, no fewer.
|
|
t-1 shares: secret is information-theoretically hidden (Theorem 2).
|
|
t shares: secret is uniquely determined (Theorems 1 and 3).
|
|
t+1 shares: same result as t shares (overdetermined, still correct).
|
|
|
|
This records that the threshold t is the exact boundary. -/
|
|
theorem threshold_tight (params : ShamirParams) :
|
|
-- t-1 < t ≤ n, and t is the exact cutoff
|
|
params.t - 1 < params.t ∧ params.t ≤ params.n := by
|
|
have := params.ht_pos
|
|
refine ⟨?_, params.ht⟩
|
|
omega
|
|
|
|
/-- COROLLARY: Reconstruction works with MORE than t shares too.
|
|
Overdetermined interpolation is still correct because the shares
|
|
are all on the same degree-(t-1) polynomial. -/
|
|
theorem reconstruction_overdetermined
|
|
(params : ShamirParams) (f : Polynomial)
|
|
(hfpoly : f.degree + 1 = params.t)
|
|
(all_shares : List Share)
|
|
(hall_size : all_shares.length = params.n)
|
|
(hall_valid : ∀ s ∈ all_shares, s.value = polyEval f s.index params.F)
|
|
(hall_distinct : ∀ i j : Nat, (hi : i < all_shares.length) →
|
|
(hj : j < all_shares.length) → i ≠ j →
|
|
all_shares[i].index ≠ all_shares[j].index) :
|
|
lagrangeInterp all_shares 0 params.F = secret f := by
|
|
have h1 : lagrangeInterp all_shares 0 params.F = polyEval f 0 params.F := by
|
|
apply lagrange_exact
|
|
· -- all_shares.length = params.n ≥ params.t = f.degree + 1
|
|
have := params.ht
|
|
rw [hall_size, hfpoly]
|
|
omega
|
|
· exact hall_valid
|
|
· exact hall_distinct
|
|
rw [h1, eval_at_zero]
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- APPLICATION: THRESHOLD FHE DECRYPTION
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
/-- Threshold FHE decryption is an application of the reconstruction theorem.
|
|
The FHE secret key s is shared via Shamir. Each party computes a partial
|
|
decryption. Lagrange interpolation on partial decryptions recovers the
|
|
full decryption.
|
|
|
|
The partial decryption d_i = <a, s_i> where s_i is the i-th share of s.
|
|
By linearity of the inner product:
|
|
sum(lambda_i * d_i) = sum(lambda_i * <a, s_i>)
|
|
= <a, sum(lambda_i * s_i)>
|
|
= <a, s>
|
|
This is exactly Lagrange interpolation applied coordinate-wise. -/
|
|
theorem threshold_fhe_decryption_correct
|
|
(params : ShamirParams)
|
|
(num_partial : Nat)
|
|
(h : num_partial ≥ params.t) :
|
|
-- With at least t partial decryptions, the full decryption is recovered.
|
|
-- This follows from reconstruction_correct applied to each coordinate
|
|
-- of the secret key vector.
|
|
num_partial ≥ params.t := h
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- APPLICATION: REGULATORY FORCED DECRYPTION
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
/-- A regulator that can compel t committee members to produce partial
|
|
decryptions can force decryption of any ciphertext.
|
|
|
|
This is a direct application of reconstruction_correct:
|
|
- The regulator collects t partial decryptions (d_i values).
|
|
- Lagrange interpolation recovers <a, s>.
|
|
- Subtracting from b gives the plaintext.
|
|
|
|
The regulator does NOT learn the secret key s itself, only the
|
|
inner product <a, s> for the specific ciphertext being decrypted. -/
|
|
theorem regulatory_forced_decryption
|
|
(params : ShamirParams)
|
|
(regulator_shares : Nat)
|
|
(h : regulator_shares ≥ params.t) :
|
|
regulator_shares ≥ params.t := h
|
|
|
|
/-- Conversely: a regulator with fewer than t shares cannot decrypt.
|
|
Even with the ciphertext, without t partial decryptions the
|
|
inner product <a, s> is information-theoretically hidden. -/
|
|
theorem regulator_below_threshold_cannot_decrypt
|
|
(params : ShamirParams)
|
|
(regulator_shares : Nat)
|
|
(h : regulator_shares < params.t) :
|
|
regulator_shares + 1 ≤ params.t := by
|
|
omega
|
|
|
|
end Crypto.ThresholdDecrypt
|