One-way Functions

# Definition 1: Inversion probability The **inversion probability** of adversary `F` against function `f` at security parameter `n` is the probability that `F` finds a preimage of `f(x)` when `x` is drawn uniformly from $\{0,1\}^n$: $$\mathrm{invertProb}(f, F, n) = \sum_{x \in \{0,1\}^n} \frac{1}{2^n} \cdot \Pr_{x' \leftarrow F(f(x))}[f(x') = f(x)]$$ Note that `F` only needs to find *some* preimage of `f(x)`, not necessarily `x` itself.
noncomputable def invertProb
    (f : BitString → BitString)
    (F : BitString → PMF BitString)
    (n : ℕ) : ℝ :=
  (∑ x : FixedBitString n,
    PMF.uniformOfFintype (FixedBitString n) x *
    (∑' x' : BitString, F (f x.toList) x' *
      (if f x' = f x.toList ∧ x'.length = n then 1 else 0)) : ENNReal).toReal
# Definition 2: One-way functions A function `f : BitString → BitString` is a **one-way function** if: 1. **Efficiency** — `f` is poly-time computable. 2. **Hardness** — for every poly-time adversary `F`, the inversion probability `invertProb f F n` is negligible in `n`. Hardness says that no efficient algorithm can invert `f` on a noticeable fraction of inputs, even when the input is chosen uniformly at random.
structure IsOneWayFunction (f : BitString → BitString) : Prop where
  polytime : PolyTimeComputable f
  hard     : ∀ F : BitString → PMF BitString, PolyTimeAdversary F →
               Negligible (invertProb f F)
# Lemma 1: A perfect inverter achieves probability 1 If `F` always returns `pure x` on input `f(x)` — i.e. it deterministically recovers the exact preimage — then `invertProb f F n = 1` for all `n`. This is the strongest possible inversion guarantee.
lemma invertProb_pure_self_eq_one
    (f : BitString → BitString) (F : BitString → PMF BitString)
    (hF : ∀ x : BitString, F (f x) = PMF.pure x)
    (n : ℕ) : invertProb f F n = 1 := by
  simp only [invertProb]
  have inner : ∀ x : FixedBitString n,
      (∑' x' : BitString, F (f x.toList) x' *
        (if f x' = f x.toList ∧ x'.length = n then (1 : ENNReal) else 0)) = 1 := by
    intro x
    rw [hF]
    have h : ∀ x' : BitString,
        (PMF.pure x.toList) x' * (if f x' = f x.toList ∧ x'.length = n then (1 : ENNReal) else 0) =
        (PMF.pure x.toList) x' := by
      intro x'
      simp only [PMF.pure_apply]
      by_cases heq : x' = x.toList
      · subst heq; simp [x.toList_length]
      · simp [heq]
    simp_rw [h]
    exact PMF.tsum_coe _
  simp_rw [inner, mul_one]
  have outer : (∑ x : FixedBitString n,
      PMF.uniformOfFintype (FixedBitString n) x) = 1 := by
    have := (PMF.uniformOfFintype (FixedBitString n)).tsum_coe
    rw [tsum_fintype (L := .unconditional _)] at this; exact this
  simp
# Lemma 2: A preimage inverter achieves probability 1 A weaker sufficient condition: if for every `x : FixedBitString n`, `F(f(x.toList))` is a point mass at some `x'` satisfying `f(x') = f(x.toList)` and `x'.length = n`, then `invertProb f F n = 1`. This handles cases where the recovered preimage may differ from `x` — for example, when `f` is not injective or when bit normalization means the canonical preimage differs from the sampled one.
lemma invertProb_pure_preimage_eq_one
    (f : BitString → BitString) (F : BitString → PMF BitString)
    (hF : ∀ (n : ℕ) (x : FixedBitString n),
            ∃ x' : BitString, F (f x.toList) = PMF.pure x' ∧ f x' = f x.toList ∧ x'.length = n)
    (n : ℕ) : invertProb f F n = 1 := by
  simp only [invertProb]
  have inner : ∀ x : FixedBitString n,
      (∑' x' : BitString, F (f x.toList) x' *
        (if f x' = f x.toList ∧ x'.length = n then (1 : ENNReal) else 0)) = 1 := by
    intro x
    obtain ⟨x', hFx, hfx', hlen⟩ := hF n x
    rw [hFx]
    have h : ∀ x'' : BitString,
        (PMF.pure x') x'' * (if f x'' = f x.toList ∧ x''.length = n then (1 : ENNReal) else 0) =
        (PMF.pure x') x'' := by
      intro x''
      simp only [PMF.pure_apply]
      by_cases heq : x'' = x'
      · subst heq; simp [hfx', hlen]
      · simp [heq]
    simp_rw [h]
    exact PMF.tsum_coe _
  simp_rw [inner, mul_one]
  have outer : (∑ x : FixedBitString n,
      PMF.uniformOfFintype (FixedBitString n) x) = 1 := by
    have := (PMF.uniformOfFintype (FixedBitString n)).tsum_coe
    rw [tsum_fintype (L := .unconditional _)] at this; exact this
  simp

Referenced by

Raw source
import Mathlib.Probability.Distributions.Uniform
import Onewayf.Adversary
import Onewayf.Negligible

-- leandown
-- [meta]
-- title = "One-way functions"
-- group = "Cryptography"
-- [content]

-- # {{definition}}: Inversion probability
--
-- The **inversion probability** of adversary `F` against function `f` at
-- security parameter `n` is the probability that `F` finds a preimage of
-- `f(x)` when `x` is drawn uniformly from $\{0,1\}^n$:
--
-- $$\mathrm{invertProb}(f, F, n) =
--   \sum_{x \in \{0,1\}^n} \frac{1}{2^n}
--   \cdot \Pr_{x' \leftarrow F(f(x))}[f(x') = f(x)]$$
--
-- Note that `F` only needs to find *some* preimage of `f(x)`, not necessarily
-- `x` itself.
--
noncomputable def invertProb
    (f : BitString → BitString)
    (F : BitString → PMF BitString)
    (n : ℕ) : ℝ :=
  (∑ x : FixedBitString n,
    PMF.uniformOfFintype (FixedBitString n) x *
    (∑' x' : BitString, F (f x.toList) x' *
      (if f x' = f x.toList ∧ x'.length = n then 1 else 0)) : ENNReal).toReal

-- # {{definition}}: One-way functions
--
-- A function `f : BitString → BitString` is a **one-way function** if:
-- 1. **Efficiency** — `f` is poly-time computable.
-- 2. **Hardness** — for every poly-time adversary `F`, the inversion
--    probability `invertProb f F n` is negligible in `n`.
--
-- Hardness says that no efficient algorithm can invert `f` on a noticeable
-- fraction of inputs, even when the input is chosen uniformly at random.
--
structure IsOneWayFunction (f : BitString → BitString) : Prop where
  polytime : PolyTimeComputable f
  hard     : ∀ F : BitString → PMF BitString, PolyTimeAdversary F →
               Negligible (invertProb f F)

-- # {{lemma}}: A perfect inverter achieves probability 1
--
-- If `F` always returns `pure x` on input `f(x)` — i.e. it deterministically
-- recovers the exact preimage — then `invertProb f F n = 1` for all `n`.
-- This is the strongest possible inversion guarantee.
--
lemma invertProb_pure_self_eq_one
    (f : BitString → BitString) (F : BitString → PMF BitString)
    (hF : ∀ x : BitString, F (f x) = PMF.pure x)
    (n : ℕ) : invertProb f F n = 1 := by
  simp only [invertProb]
  have inner : ∀ x : FixedBitString n,
      (∑' x' : BitString, F (f x.toList) x' *
        (if f x' = f x.toList ∧ x'.length = n then (1 : ENNReal) else 0)) = 1 := by
    intro x
    rw [hF]
    have h : ∀ x' : BitString,
        (PMF.pure x.toList) x' * (if f x' = f x.toList ∧ x'.length = n then (1 : ENNReal) else 0) =
        (PMF.pure x.toList) x' := by
      intro x'
      simp only [PMF.pure_apply]
      by_cases heq : x' = x.toList
      · subst heq; simp [x.toList_length]
      · simp [heq]
    simp_rw [h]
    exact PMF.tsum_coe _
  simp_rw [inner, mul_one]
  have outer : (∑ x : FixedBitString n,
      PMF.uniformOfFintype (FixedBitString n) x) = 1 := by
    have := (PMF.uniformOfFintype (FixedBitString n)).tsum_coe
    rw [tsum_fintype (L := .unconditional _)] at this; exact this
  simp

-- # {{lemma}}: A preimage inverter achieves probability 1
--
-- A weaker sufficient condition: if for every `x : FixedBitString n`, `F(f(x.toList))` is a
-- point mass at some `x'` satisfying `f(x') = f(x.toList)` and `x'.length = n`,
-- then `invertProb f F n = 1`.
-- This handles cases where the recovered preimage may differ from `x` — for
-- example, when `f` is not injective or when bit normalization means the
-- canonical preimage differs from the sampled one.
--
lemma invertProb_pure_preimage_eq_one
    (f : BitString → BitString) (F : BitString → PMF BitString)
    (hF : ∀ (n : ℕ) (x : FixedBitString n),
            ∃ x' : BitString, F (f x.toList) = PMF.pure x' ∧ f x' = f x.toList ∧ x'.length = n)
    (n : ℕ) : invertProb f F n = 1 := by
  simp only [invertProb]
  have inner : ∀ x : FixedBitString n,
      (∑' x' : BitString, F (f x.toList) x' *
        (if f x' = f x.toList ∧ x'.length = n then (1 : ENNReal) else 0)) = 1 := by
    intro x
    obtain ⟨x', hFx, hfx', hlen⟩ := hF n x
    rw [hFx]
    have h : ∀ x'' : BitString,
        (PMF.pure x') x'' * (if f x'' = f x.toList ∧ x''.length = n then (1 : ENNReal) else 0) =
        (PMF.pure x') x'' := by
      intro x''
      simp only [PMF.pure_apply]
      by_cases heq : x'' = x'
      · subst heq; simp [hfx', hlen]
      · simp [heq]
    simp_rw [h]
    exact PMF.tsum_coe _
  simp_rw [inner, mul_one]
  have outer : (∑ x : FixedBitString n,
      PMF.uniformOfFintype (FixedBitString n) x) = 1 := by
    have := (PMF.uniformOfFintype (FixedBitString n)).tsum_coe
    rw [tsum_fintype (L := .unconditional _)] at this; exact this
  simp