Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cslib.lean
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,5 @@ public import Cslib.Logics.Modal.Denotation
public import Cslib.Logics.Propositional.Defs
public import Cslib.Logics.Propositional.NaturalDeduction.Basic
public import Cslib.MachineLearning.PACLearning.Defs
public import Cslib.Probability.HasUniform
public import Cslib.Probability.PMF
7 changes: 4 additions & 3 deletions Cslib/Crypto/Protocols/PerfectSecrecy/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,20 @@ Characterisation theorems for perfect secrecy following
namespace Cslib.Crypto.Protocols.PerfectSecrecy.EncScheme

universe u
variable {M K C : Type u}
variable {n : Type u → Type*} [MonadLiftT n PMF] [Probability.HasUniformSelectFinset n]
{M K C : Type u}

/-- A scheme is perfectly secret iff the ciphertext distribution is
independent of the plaintext ([KatzLindell2020], Lemma 2.5). -/
theorem perfectlySecret_iff_ciphertextIndist (scheme : EncScheme M K C) :
theorem perfectlySecret_iff_ciphertextIndist (scheme : EncScheme n M K C) :
scheme.PerfectlySecret ↔ scheme.CiphertextIndist :=
⟨PerfectSecrecy.ciphertextIndist_of_perfectlySecret scheme,
PerfectSecrecy.perfectlySecret_of_ciphertextIndist scheme⟩

/-- Perfect secrecy requires `|K| ≥ |M|`
([KatzLindell2020], Theorem 2.12). -/
theorem perfectlySecret_keySpace_ge [Finite K]
(scheme : EncScheme M K C) (h : scheme.PerfectlySecret) :
(scheme : EncScheme n M K C) (h : scheme.PerfectlySecret) :
Nat.card K ≥ Nat.card M :=
PerfectSecrecy.shannonKeySpace scheme h

Expand Down
24 changes: 12 additions & 12 deletions Cslib/Crypto/Protocols/PerfectSecrecy/Defs.lean
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,33 @@ Core definitions for perfect secrecy following [KatzLindell2020], Chapter 2.
namespace Cslib.Crypto.Protocols.PerfectSecrecy.EncScheme

universe u
variable {M K C : Type u}
variable {n : Type u → Type*} [MonadLiftT n PMF] {M K C : Type u}

/-- The distribution of `Enc_K(m)` when `K ← Gen`. -/
noncomputable def ciphertextDist (scheme : EncScheme M K C) (m : M) : PMF C := do
noncomputable def ciphertextDist (scheme : EncScheme n M K C) (m : M) : PMF C := do
scheme.enc (← scheme.gen) m

/-- Joint distribution of `(M, C)` given a message prior. -/
noncomputable def jointDist (scheme : EncScheme M K C) (msgDist : PMF M) : PMF (M × C) := do
noncomputable def jointDist (scheme : EncScheme n M K C) (msgDist : n M) : PMF (M × C) := do
let m ← msgDist
return (m, ← scheme.ciphertextDist m)

/-- Marginal ciphertext distribution given a message prior. -/
noncomputable def marginalCiphertextDist (scheme : EncScheme M K C)
(msgDist : PMF M) : PMF C := do
noncomputable def marginalCiphertextDist (scheme : EncScheme n M K C)
(msgDist : n M) : PMF C := do
scheme.ciphertextDist (← msgDist)

/-- The posterior message distribution `Pr[M | C = c]` as a probability
distribution, given a message prior and a ciphertext in the support of
the marginal distribution. -/
noncomputable def posteriorMsgDist (scheme : EncScheme M K C)
(msgDist : PMF M) (c : C)
noncomputable def posteriorMsgDist (scheme : EncScheme n M K C)
(msgDist : n M) (c : C)
(hc : c ∈ (scheme.marginalCiphertextDist msgDist).support) : PMF M :=
Cslib.Probability.PMF.posteriorDist msgDist scheme.ciphertextDist c hc

@[simp]
theorem posteriorMsgDist_apply (scheme : EncScheme M K C)
(msgDist : PMF M) (c : C)
theorem posteriorMsgDist_apply (scheme : EncScheme n M K C)
(msgDist : n M) (c : C)
(hc : c ∈ (scheme.marginalCiphertextDist msgDist).support) (m : M) :
scheme.posteriorMsgDist msgDist c hc m =
scheme.jointDist msgDist (m, c) / scheme.marginalCiphertextDist msgDist c :=
Expand All @@ -71,14 +71,14 @@ theorem posteriorMsgDist_apply (scheme : EncScheme M K C)
/-- An encryption scheme is perfectly secret if the posterior message
distribution equals the prior for every ciphertext with positive probability
([KatzLindell2020], Definition 2.3). -/
def PerfectlySecret (scheme : EncScheme M K C) : Prop :=
∀ (msgDist : PMF M) (c : C)
def PerfectlySecret (scheme : EncScheme n M K C) : Prop :=
∀ (msgDist : n M) (c : C)
(hc : c ∈ (scheme.marginalCiphertextDist msgDist).support),
scheme.posteriorMsgDist msgDist c hc = msgDist

/-- Ciphertext indistinguishability: the ciphertext distribution is the same
for all messages ([KatzLindell2020], Lemma 2.5). -/
def CiphertextIndist (scheme : EncScheme M K C) : Prop :=
def CiphertextIndist (scheme : EncScheme n M K C) : Prop :=
∀ m₀ m₁ : M, scheme.ciphertextDist m₀ = scheme.ciphertextDist m₁

end Cslib.Crypto.Protocols.PerfectSecrecy.EncScheme
29 changes: 16 additions & 13 deletions Cslib/Crypto/Protocols/PerfectSecrecy/Encryption.lean
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/-
Copyright (c) 2026 Samuel Schlesinger. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Samuel Schlesinger
Authors: Samuel Schlesinger, Devon Tuma
-/

module

public import Cslib.Init
public import Mathlib.Probability.ProbabilityMassFunction.Monad
public import Cslib.Probability.PMF

/-!
# Private-Key Encryption Schemes (Information-Theoretic)
Expand Down Expand Up @@ -35,27 +34,31 @@ namespace Cslib.Crypto.Protocols.PerfectSecrecy
A private-key encryption scheme over message space `M`, key space `K`,
and ciphertext space `C` ([KatzLindell2020], Definition 2.1).
-/
structure EncScheme (Message Key Ciphertext : Type*) where
structure EncScheme (m : Type u → Type*) [MonadLiftT m PMF]
(Message Key Ciphertext : Type u) where
/-- Probabilistic key generation. -/
gen : PMF Key
gen : m Key
/-- (Possibly randomized) encryption. -/
enc (key : Key) (message : Message) : PMF Ciphertext
enc (key : Key) (message : Message) : m Ciphertext
/-- Deterministic decryption. -/
dec (key : Key) (ciphertext : Ciphertext) : Message
/-- Decryption inverts encryption for all keys in the support of `gen`. -/
correct : ∀ key, key ∈ gen.support → ∀ message ciphertext,
ciphertext ∈ (enc key message).support → dec key ciphertext = message
correct : ∀ key, key ∈ PMF.support gen → ∀ message ciphertext,
ciphertext ∈ PMF.support (enc key message) → dec key ciphertext = message

/-- Build an encryption scheme from deterministic pure encryption/decryption
where decryption is a left inverse of encryption for every key. -/
noncomputable def EncScheme.ofPure.{u} {Message Key Ciphertext : Type u} (gen : PMF Key)
noncomputable def EncScheme.ofPure.{u} {m : Type u → Type*}
[Monad m] [MonadLiftT m PMF] [LawfulMonadLiftT m PMF]
{Message Key Ciphertext : Type u} (gen : m Key)
(enc : Key → Message → Ciphertext) (dec : Key → Ciphertext → Message)
(h : ∀ key, Function.LeftInverse (dec key) (enc key)) :
EncScheme Message Key Ciphertext where
EncScheme m Message Key Ciphertext where
gen := gen
enc key message := PMF.pure (enc key message)
enc key message := pure (enc key message)
dec := dec
correct key _ message _ hc := by
rw [PMF.mem_support_pure_iff] at hc; subst hc; exact h key message
correct key _ message ciphertext hc := by
have : ciphertext = enc key message := by simpa using hc
subst this; exact h key message

end Cslib.Crypto.Protocols.PerfectSecrecy
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/-
Copyright (c) 2026 Samuel Schlesinger. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Samuel Schlesinger
Authors: Samuel Schlesinger, Devon Tuma
-/

module

public import Cslib.Crypto.Protocols.PerfectSecrecy.Defs
public import Cslib.Probability.HasUniform
public import Mathlib.Probability.Distributions.Uniform

/-!
Expand All @@ -26,26 +27,26 @@ namespace Cslib.Crypto.Protocols.PerfectSecrecy
open PMF ENNReal

universe u
variable {M K C : Type u}
variable {n : Type u → Type*} [MonadLiftT n PMF] {M K C : Type u}

/-- The joint distribution at `(m, c)` equals `msgDist m * ciphertextDist m c`. -/
theorem jointDist_eq (scheme : EncScheme M K C) (msgDist : PMF M)
theorem jointDist_eq (scheme : EncScheme n M K C) (msgDist : n M)
(m : M) (c : C) :
scheme.jointDist msgDist (m, c) = msgDist m * scheme.ciphertextDist m c :=
scheme.jointDist msgDist (m, c) = (msgDist : PMF M) m * scheme.ciphertextDist m c :=
Cslib.Probability.PMF.bind_pair_apply msgDist scheme.ciphertextDist m c

/-- Summing the joint distribution over messages gives the marginal ciphertext distribution. -/
theorem jointDist_tsum_fst (scheme : EncScheme M K C) (msgDist : PMF M) (c : C) :
theorem jointDist_tsum_fst (scheme : EncScheme n M K C) (msgDist : n M) (c : C) :
∑' m, scheme.jointDist msgDist (m, c) = scheme.marginalCiphertextDist msgDist c :=
Cslib.Probability.PMF.bind_pair_tsum_fst msgDist scheme.ciphertextDist c

/-- Perfect secrecy is equivalent to message-ciphertext independence.
The two formulations are related by multiplying/dividing by `marginal(c)`. -/
theorem perfectlySecret_iff_indep (scheme : EncScheme M K C) :
theorem perfectlySecret_iff_indep (scheme : EncScheme n M K C) :
scheme.PerfectlySecret ↔
∀ (msgDist : PMF M) (m : M) (c : C),
∀ (msgDist : n M) (m : M) (c : C),
scheme.jointDist msgDist (m, c) =
msgDist m * scheme.marginalCiphertextDist msgDist c := by
(msgDist : PMF M) m * scheme.marginalCiphertextDist msgDist c := by
constructor
· intro h msgDist m c
by_cases hc : (scheme.marginalCiphertextDist msgDist) c = 0
Expand All @@ -64,11 +65,11 @@ theorem perfectlySecret_iff_indep (scheme : EncScheme M K C) :
(ne_top_of_le_ne_top one_ne_top (PMF.coe_le_one _ c))]

/-- Ciphertext indistinguishability implies message-ciphertext independence. -/
theorem indep_of_ciphertextIndist (scheme : EncScheme M K C)
theorem indep_of_ciphertextIndist (scheme : EncScheme n M K C)
(h : scheme.CiphertextIndist)
(msgDist : PMF M) (m : M) (c : C) :
(msgDist : n M) (m : M) (c : C) :
scheme.jointDist msgDist (m, c) =
msgDist m * scheme.marginalCiphertextDist msgDist c := by
(msgDist : PMF M) m * scheme.marginalCiphertextDist msgDist c := by
rw [jointDist_eq]; congr 1
change scheme.ciphertextDist m c =
PMF.bind msgDist (fun m' => scheme.ciphertextDist m') c
Expand All @@ -77,50 +78,54 @@ theorem indep_of_ciphertextIndist (scheme : EncScheme M K C)
rw [ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul]

/-- Ciphertext indistinguishability implies perfect secrecy. -/
theorem perfectlySecret_of_ciphertextIndist (scheme : EncScheme M K C)
theorem perfectlySecret_of_ciphertextIndist (scheme : EncScheme n M K C)
(h : scheme.CiphertextIndist) :
scheme.PerfectlySecret :=
(perfectlySecret_iff_indep scheme).mpr (fun msgDist m c =>
indep_of_ciphertextIndist scheme h msgDist m c)

/-- Perfect secrecy implies ciphertext indistinguishability. -/
theorem ciphertextIndist_of_perfectlySecret (scheme : EncScheme M K C)
(h : scheme.PerfectlySecret) :
/-- Perfect secrecy implies ciphertext indistinguishability.
Note we need `n` to support uniform selection for the proof to work -/
theorem ciphertextIndist_of_perfectlySecret [Probability.HasUniformSelectFinset n]
(scheme : EncScheme n M K C) (h : scheme.PerfectlySecret) :
scheme.CiphertextIndist := by
classical
rw [perfectlySecret_iff_indep] at h
intro m₀ m₁; ext c
have hs : ({m₀, m₁} : Finset M).Nonempty := ⟨m₀, Finset.mem_insert_self ..⟩
set μ := PMF.uniformOfFinset _ hs
let μ : n M := Probability.HasUniformSelectFinset.uniformSelectFinset _ hs
have hμ : (μ : PMF M) = PMF.uniformOfFinset _ hs := by simp [μ]
suffices key : ∀ m ∈ ({m₀, m₁} : Finset M),
scheme.ciphertextDist m c = scheme.marginalCiphertextDist μ c by
exact (key m₀ (by simp)).trans (key m₁ (by simp)).symm
intro m hm
have hne := (PMF.mem_support_uniformOfFinset_iff hs m).mpr hm
have hne_top := ne_top_of_le_ne_top one_ne_top (PMF.coe_le_one μ m)
exact (ENNReal.mul_right_inj hne hne_top).mp (by rw [← jointDist_eq]; exact h μ m c)
rw [Probability.HasUniformSelectFinset.liftM_uniformSelectFinset] at hne_top
refine (ENNReal.mul_right_inj hne hne_top).mp ?_
exact (hμ ▸ jointDist_eq scheme _ m c).symm.trans (hμ ▸ h μ m c)

/-- If each message maps to a key that encrypts it to a common ciphertext,
then the key assignment is injective (by correctness of decryption). -/
lemma encrypt_key_injective (scheme : EncScheme M K C)
lemma encrypt_key_injective (scheme : EncScheme n M K C)
(f : M → K) (c₀ : C)
(hf_mem : ∀ m, f m ∈ scheme.gen.support)
(hf_enc : ∀ m, c₀ ∈ (scheme.enc (f m) m).support) :
(hf_mem : ∀ m, f m ∈ PMF.support scheme.gen)
(hf_enc : ∀ m, c₀ ∈ PMF.support (scheme.enc (f m) m)) :
Function.Injective f :=
fun m₁ m₂ heq =>
(scheme.correct _ (hf_mem m₁) m₁ c₀ (hf_enc m₁)).symm.trans
(heq ▸ scheme.correct _ (hf_mem m₂) m₂ c₀ (hf_enc m₂))

/-- Perfect secrecy requires `|K| ≥ |M|` (Shannon's theorem). -/
theorem shannonKeySpace [Finite K]
(scheme : EncScheme M K C) (h : scheme.PerfectlySecret) :
theorem shannonKeySpace [Finite K] [Probability.HasUniformSelectFinset n]
(scheme : EncScheme n M K C) (h : scheme.PerfectlySecret) :
Nat.card K ≥ Nat.card M := by
classical
have hci := ciphertextIndist_of_perfectlySecret scheme h
by_cases hM : IsEmpty M; · simp
obtain ⟨m₀⟩ := not_isEmpty_iff.mp hM
obtain ⟨c₀, hc₀⟩ := (scheme.ciphertextDist m₀).support_nonempty
have key_exists : ∀ m, ∃ k ∈ scheme.gen.support, c₀ ∈ (scheme.enc k m).support := by
have key_exists : ∀ m, ∃ k ∈ PMF.support scheme.gen, c₀ ∈ PMF.support (scheme.enc k m) := by
intro m
exact (PMF.mem_support_bind_iff _ _ _).mp
(show c₀ ∈ (scheme.ciphertextDist m).support by rw [hci m m₀]; exact hc₀)
Expand Down
2 changes: 1 addition & 1 deletion Cslib/Crypto/Protocols/PerfectSecrecy/OneTimePad.lean
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ namespace Cslib.Crypto.Protocols.PerfectSecrecy
/-- The one-time pad over `l`-bit strings. Encryption and decryption
are XOR ([KatzLindell2020], Construction 2.9). -/
noncomputable def otp (l : ℕ) :
EncScheme (BitVec l) (BitVec l) (BitVec l) :=
EncScheme PMF (BitVec l) (BitVec l) (BitVec l) :=
.ofPure (PMF.uniformOfFintype _) (· ^^^ ·) (· ^^^ ·) fun k m => by
simp [← BitVec.xor_assoc]

Expand Down
55 changes: 55 additions & 0 deletions Cslib/Probability/HasUniform.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/-
Copyright (c) 2026 Devon Tuma. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Devon Tuma
-/

module

public import Cslib.Init
public import Mathlib.Probability.Distributions.Uniform

/-!
# Monads with Uniform Selection
This file defines general typeclasses for asserting that a monad `m` with an embedding into `PMF`
can model computations that lift to uniform distributions in `PMF`.
## Main Definitions
- `Cslib.Probability.HasUniformSelectFinset`: monad supports uniform finset selection
- `Cslib.Probability.HasUniformSelectFintype`: monad supports uniform finite types
-/

@[expose] public section

namespace Cslib.Probability

universe u v

/-- The monad `m` has a way to model uniform selection over non-empty finsets. -/
class HasUniformSelectFinset (m : Type u → Type*) [MonadLiftT m PMF] where
uniformSelectFinset {α : Type u} (s : Finset α) (hs : s.Nonempty) : m α
liftM_uniformSelectFinset {α : Type u} (s : Finset α) (hs : s.Nonempty) :
(uniformSelectFinset s hs : PMF α) = PMF.uniformOfFinset s hs

attribute [simp] HasUniformSelectFinset.liftM_uniformSelectFinset

noncomputable instance : HasUniformSelectFinset PMF where
uniformSelectFinset := PMF.uniformOfFinset
liftM_uniformSelectFinset _ _ := rfl

/-- The monad `m` has a way to model uniform selection over non-empty finsets. -/
class HasUniformSelectFintype (m : Type u → Type*) [MonadLiftT m PMF] where
uniformSelectFintype (α : Type u) [Fintype α] [Nonempty α] : m α
liftM_uniformSelectFinset (α : Type u) [Fintype α] [Nonempty α] :
(uniformSelectFintype α : PMF α) = PMF.uniformOfFintype α

attribute [simp] HasUniformSelectFinset.liftM_uniformSelectFinset

noncomputable instance : HasUniformSelectFintype PMF where
uniformSelectFintype := PMF.uniformOfFintype
liftM_uniformSelectFinset _ _ := rfl

end Cslib.Probability
3 changes: 3 additions & 0 deletions Cslib/Probability/PMF.lean
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,7 @@ theorem posteriorDist_eq_prior_of_outputIndist (p : PMF α) (f : α → PMF β)
exact ENNReal.mul_div_cancel_right (hmarg ▸ hb')
(ne_top_of_le_ne_top ENNReal.one_ne_top (PMF.coe_le_one _ _))

@[simp]
lemma monad_pure_eq_pure (x : α) : (pure x : PMF α) = PMF.pure x := rfl

end Cslib.Probability.PMF