Cryptography
Hashing and collisions
Use Lean’s built-in hash and see why a hash match is not proof of equality.
Open Hashing.lean in editor-- hash is in Lean's core library. No import is needed.
-- It returns a UInt64 using a Hashable instance for the input type.
def fingerprint (text : String) : UInt64 := hash text
def main : IO Unit := do
let original := fingerprint "Lean"
let unchanged := fingerprint "Lean"
let changed := fingerprint "lean"
IO.println s!"Same input: {original == unchanged}"
IO.println s!"Changed input: {original == changed}"
-- For Nat, hash simply wraps the number to 64 bits.
-- These distinct numbers therefore have the same hash.
let a : Nat := 0
let b : Nat := 2^64
IO.println s!"Equal values: {a == b}"
IO.println s!"Equal hashes: {hash a == hash b}"
-- A collision means distinct inputs have the same hash.
-- Lean's hash is for data structures, not cryptographic commitments.
-- Open the SHA-256 example for a cryptographic hash algorithm.
Explanation
hash · Hashable
hash asks the input type’s Hashable instance for a 64-bit hash. The implementation depends on the type and is not a cryptographic guarantee.
UInt64
An unsigned 64-bit integer. There are only 2^64 possible outputs, so hashing unbounded inputs must allow collisions.
hash a == hash b
Equal hashes do not establish a = b. This program shows a concrete collision.
Expected output
Reference only. Run the file to check its actual result.
Same input: true Changed input: false Equal values: false Equal hashes: true
Exercise
Print fingerprint "Lean". Then hash 1 and 2^64 + 1 as natural numbers to find another collision.