Skip to content

feat(Cryptography): formalise perfect secrecy and the one-time pad#464

Open
SamuelSchlesinger wants to merge 3 commits intoleanprover:mainfrom
SamuelSchlesinger:perfect-secrecy
Open

feat(Cryptography): formalise perfect secrecy and the one-time pad#464
SamuelSchlesinger wants to merge 3 commits intoleanprover:mainfrom
SamuelSchlesinger:perfect-secrecy

Conversation

@SamuelSchlesinger
Copy link
Copy Markdown

Adds Cslib.Cryptography.PerfectSecrecy with information-theoretic private-key encryption schemes and perfect secrecy following Katz-Lindell, Chapter 2:

  • EncScheme: private-key encryption (Definition 2.1)
  • PerfectlySecret: perfect secrecy (Definition 2.3)
  • perfectlySecret_iff_ciphertextIndist: ciphertext indistinguishability characterization (Lemma 2.5)
  • otp: the one-time pad construction (Construction 2.9)
  • otp_perfectlySecret: the OTP is perfectly secret (Theorem 2.10)
  • perfectlySecret_keySpace_ge: Shannon's theorem, |K| ≥ |M| (Theorem 2.12)

For some context, Katz reviewed this in its original home: SamuelSchlesinger/introduction-to-modern-cryptography#1.

Adds `Cslib.Cryptography.PerfectSecrecy` with information-theoretic
private-key encryption schemes and perfect secrecy following
Katz-Lindell, Chapter 2:

- `EncScheme`: private-key encryption (Definition 2.1)
- `PerfectlySecret`: perfect secrecy (Definition 2.3)
- `perfectlySecret_iff_ciphertextIndist`: ciphertext indistinguishability
  characterization (Lemma 2.5)
- `otp`: the one-time pad construction (Construction 2.9)
- `otp_perfectlySecret`: the OTP is perfectly secret (Theorem 2.10)
- `perfectlySecret_keySpace_ge`: Shannon's theorem, |K| ≥ |M|
  (Theorem 2.12)
@SamuelSchlesinger
Copy link
Copy Markdown
Author

I'd be quite interested in helping with future review in this Cryptography folder, if there is interest in including it.

PMF.uniformOfFintype (Fin l → Bool) := by
ext c
simp only [PMF.bind_apply, PMF.uniformOfFintype_apply, PMF.pure_apply]
have heq : ∀ a : Fin l → Bool,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be a lemma of some kind (might be easier in BitVec, not sure).

-/
structure EncScheme.{u} (M K C : Type u) where
/-- Probabilistic key generation. -/
gen : PMF K
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have briefly spoken about this, but it is worth considering whether rational probabilities might be desirable. The concern would be that sometimes you do want to be able to take limits of probabilities etc.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think given that this is really not going to be reused very much (the rest of the book defined encryption schemes totally differently), I feel we shouldn't define a custom notion of rational distributions just for this.

That said, I am very open to computable probability distributions being defined somewhere if they're going to be useful broadly.

/-- Deterministic decryption. -/
dec : K → C → M
/-- Decryption inverts encryption for all keys in the support of `gen`. -/
correct : ∀ k, k ∈ gen.support → ∀ m c, c ∈ (enc k m).support → dec k c = m
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like the right statement of correctness to me. I wonder if "gen.support" (the set of valid keys) and (enc k m),support (the set of valid encryptions for a give k) want names/definitions (or at least abbreviations).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, support feels like the right phrasing, but I am open to suggestions.

independent of the plaintext ([KatzLindell2021], Lemma 2.5). -/
theorem perfectlySecret_iff_ciphertextIndist (scheme : EncScheme M K C) :
scheme.PerfectlySecret ↔
∀ m₀ m₁ : M, scheme.ciphertextDist m₀ = scheme.ciphertextDist m₁ :=
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems sensible to me and must be a pattern that occurs elsewhere.

def PerfectlySecret (scheme : EncScheme M K C) : Prop :=
∀ (msgDist : PMF M) (m : M) (c : C),
c ∈ (scheme.marginalCiphertextDist msgDist).support →
scheme.posteriorMsgProb msgDist c m = msgDist m
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very nearly equivalent to:
∀ (msgDist : PMF M) (c : C),
c ∈ (scheme.marginalCiphertextDist msgDist).support →
scheme.posteriorMsgProb msgDist c = msgDist

The issue is that the types don't match. But I think scheme.posteriorMsgProb msgDist c is a distribution!


/-- Posterior probability `Pr[M = m | C = c]`. -/
noncomputable def posteriorMsgProb (scheme : EncScheme M K C)
(msgDist : PMF M) (c : C) (m : M) : ENNReal :=
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think scheme.posteriorMsgProb msgDist c is a distribution. It would be good to define it in these terms. It will require a little proof (but do we not already have this in Mathlib? Maybe not).


/-- The one-time pad over `l`-bit strings. Encryption and decryption
are pointwise XOR ([KatzLindell2021], Construction 2.9). -/
noncomputable def otp (l : ℕ) : EncScheme (Fin l → Bool) (Fin l → Bool) (Fin l → Bool) where
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this. However I do want to suggest that you could do this a different way - one could start with the (uniform) bernoulli distribution on Bool and show the most trivial case (when l = 1). Then maybe there's a way to meaningfully take the "product". Thought I'm not sure the concept of a product of an encryption scheme exists in the literature (I would argue it's perfectly sensible though).

variable {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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really do feel this ought to be in Mathlib (sans the encryption context) because I can't see that anything here uses the particular properties of the encryption scheme?

rw [ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul]

/-- Ciphertext indistinguishability implies perfect secrecy. -/
theorem backward (scheme : EncScheme M K C)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a good theorem name.

/-- Perfect secrecy requires `|K| ≥ |M|` (Shannon's theorem). -/
theorem shannonKeySpace [Fintype M] [Fintype K]
(scheme : EncScheme M K C) (h : scheme.PerfectlySecret) :
Fintype.card K ≥ Fintype.card M := by
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be true with Nat.card?

have : c₀ ∈ (scheme.ciphertextDist m).support := by rw [hci m m₀]; exact hc₀
exact (PMF.mem_support_bind_iff _ _ _).mp this
choose f hf_mem hf_enc using key_exists
exact Fintype.card_le_of_injective f fun m₁ m₂ heq => by
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see an injectivity proof in a theorem, I think it should be a lemma (even if it is on the choice here).

Copy link
Copy Markdown

@linesthatinterlace linesthatinterlace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general I think this is a good start on this stuff. My concern right now is about theorem organization and structure (including naming), and about whether any of the results you've proved should be Mathlib results or at least separated out (i.e. they are not really results about cryptographic concepts specifically).

@SamuelSchlesinger
Copy link
Copy Markdown
Author

In general I think this is a good start on this stuff. My concern right now is about theorem organization and structure (including naming), and about whether any of the results you've proved should be Mathlib results or at least separated out (i.e. they are not really results about cryptographic concepts specifically).

Thanks for the feedback! I'll work on a follow up commit today that addresses each of these concerns. Re: Mathlib results, not totally sure how to approach it, I'll wait for more advice there.

@fmontesi
Copy link
Copy Markdown
Collaborator

fmontesi commented Apr 1, 2026

Regarding the directory name, is it open also to cryptology stuff in the future? If so, we could consider calling it simply 'Crypto'.

@SamuelSchlesinger
Copy link
Copy Markdown
Author

Regarding the directory name, is it open also to cryptology stuff in the future? If so, we could consider calling it simply 'Crypto'.

Great idea. I was actually considering adding some of the ciphers from the first chapter of the book originally, that could be fun to have around pedagogically.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double directory name?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PerfectSecrecy is used a lot as directory and file name. :-)
Here it looks a bit generic. Could this be called something else?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll fix that similarly to the double directory name. Comes from the copy-paste from the previous home.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of this file?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another copy-paste overlook, will reorganize.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah no its not, its intentional. Its where we re-export all of these things from. I will add a doc comment.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should just be removed completely, we avoid this pattern in general.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me.

Addresses reviewer comments from linesthatinterlace and fmontesi.

Definitions and naming:
- Define `CiphertextIndist` predicate for ciphertext indistinguishability
- Define `posteriorMsgDist : PMF M` proving the posterior is a valid
  distribution, with `perfectlySecret_iff_posteriorEq`
- Rename `backward`/`forward` to `perfectlySecret_of_ciphertextIndist`
  and `ciphertextIndist_of_perfectlySecret`
- Extract `xor_right_eq_iff` and `encrypt_key_injective` as lemmas

OTP and types:
- Use `BitVec l` instead of `Fin l → Bool` for the one-time pad,
  with a `Fintype (BitVec n)` instance via `Fin (2^n)`

Generalisations:
- Weaken Shannon's bound from `[Fintype]` to `[Finite]` with `Nat.card`
- Shorten `perfectlySecret_iff_indep` proof

Housekeeping:
- Add KatzLindell2020 to references.bib (3rd ed., CRC Press, 2020)
- Fix citation key from KatzLindell2021 to KatzLindell2020
- Eliminate `PerfectSecrecy/PerfectSecrecy/` double directory
- Remove umbrella re-export module (not a pattern used in cslib)
- Revert unrelated CI workflow and .gitignore changes
@SamuelSchlesinger
Copy link
Copy Markdown
Author

I believe all the current feedback should be addressed now, let me know if there are more nits or issues.


namespace Cslib.Crypto.PerfectSecrecy.OTP

noncomputable instance (n : ℕ) : Fintype (BitVec n) :=
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No point having a Fintype instance be non-computable. Surprised this even needs to be. I would suggest actually FinEnum is most suitable here - and that could probably be added in Mathlib.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah oops I was meaning to remove this before pushing. I'm nearly sure this is an unnecessary orphan, I bet its somewhere on Mathlib.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, it doesn't seem to be upstream AFAICT. I think you're right that we should upstream this as a FinEnum instance.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to try to sidestep the need for it for now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not following the details here, but if you need an instance that should eventually be upstreamed, this is fine to have in CSLib. We just ask that you leave a TODO comment. (There's an action configured that opens issues for these with links to the source code.)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't quite sidestep it, but at least made it local.


/-- XOR by a fixed mask is self-inverse on `BitVec`: `c = k ^^^ m ↔ k = c ^^^ m`. -/
lemma xor_right_eq_iff {l : ℕ} (c m k : BitVec l) :
(c = k ^^^ m) ↔ (k = c ^^^ m) := by
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a special tactic to discharge bitvec goals. Ask around (I am not sure). This should definitely be upstream.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh neat. I can grep around in Mathlib for it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bv_decide doesn't seem to work, if that's what you meant

- Remove unused `[Finite M]` from `shannonKeySpace` /
  `perfectlySecret_keySpace_ge` (caught by unusedArguments linter).
- Replace orphan `Fintype (BitVec n)` instance with a named
  `@[reducible] def bitVecFintype`, used locally via `haveI`.
rw [BitVec.xor_assoc, BitVec.xor_self, BitVec.xor_zero]

/-- The ciphertext distribution of the OTP is uniform, regardless of the message. -/
theorem otp_ciphertextDist_eq_uniform (l : ℕ) (m : BitVec l) :
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

module

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

@[expose] public section

/-!
# One-Time Pad: Internal proofs

The OTP ciphertext distribution is uniform regardless of message.
-/

namespace Cslib.Crypto.PerfectSecrecy.OTP

instance (n : ℕ) : Fintype (BitVec n) :=
  Fintype.ofEquiv (Fin (2 ^ n)) BitVec.equivFin.symm.toEquiv

/-- XOR by a fixed mask is self-inverse on `BitVec`: `c = k ^^^ m ↔ k = c ^^^ m`. -/
lemma xor_right_eq_iff {l : ℕ} (c m k : BitVec l) :
    (c = k ^^^ m) ↔ (k = c ^^^ m) := by grind

/-- The ciphertext distribution of the OTP is uniform, regardless of the message. -/
theorem otp_ciphertextDist_eq_uniform {l : ℕ} (m : BitVec l) :
    (PMF.uniformOfFintype _).bind (PMF.pure <| · ^^^ m) = PMF.uniformOfFintype _ := by
  simp [PMF.ext_iff, xor_right_eq_iff]

end Cslib.Crypto.PerfectSecrecy.OTP

Copy link
Copy Markdown

@linesthatinterlace linesthatinterlace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posted some golfing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants