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.
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
|